Существует ли шаблон или рекомендуемый способ ожидания значения generi c для достижения определенного значения? - PullRequest
2 голосов
/ 17 апреля 2020

В этом дивном новом асиновом мире c я снова и снова нуждаюсь в приемлемом методе ожидания чего-то, чтобы быть в определенном состоянии или соответствовать определенному условию. Например (псевдокод)

await (myStateMachine.State == StateEnum.Ready);

await (myDownloadProgress == 100.0);

await (mySpiDeviceFifoLEvel != 0);

Эти сценарии ios возникают из-за необходимости удерживать некоторый асинхронно запущенный код, пока не будет достигнуто определенное состояние в другой части кода. Например, пользователь запускает новую часть пользовательского интерфейса, но фоновый поток все еще пытается установить связь sh с частью аппаратного обеспечения. Или конечный автомат, управляющий одним аппаратным обеспечением, должен ждать, пока другой конечный автомат, управляющий другим аппаратным обеспечением, не достигнет определенного состояния готовности. и при этом заметил появление определенных шаблонов, поэтому естественная прогрессия заключается в том, чтобы закодировать нам некоторый вспомогательный класс / generi c для выполнения такого рода поведения повторно.

До того, как я go упаду На этом маршруте должны быть другие, которые решают эту проблему, поэтому мне было интересно, знает ли кто-нибудь об испытанном и проверенном шаблоне или рекомендованном способе сделать это. Я провел некоторые поиски на WWW but и не нашел ничего особенно убедительного. Этот ТАК вопрос затрагивает эту тему, но операционная служба задает другую причину. Этот ТАК * вопрос требует такого же рода вещей, но указывает c на выполнение задачи.

Способы, которые я до сих пор достигал

1. Не делай этого! Используйте событие

Когда я контролирую источник (например, состояние конечного автомата), который меняется, я часто убеждаю себя, что делаю это неправильно, и вместо того, чтобы ждать достижения значения Я должен заставить производителя (конечный автомат) генерировать событие, когда мое условие будет достигнуто. Любой слушатель может затем использовать AutoResetEvent или ManualResetEvent для ожидания обработчика

{
  myStateMachine.OnMyConditionAchieved += OnConditionAchievedEventHandler;
  myEvent = new AutoResetEvent(false);
  myEvent.WaitOne();
}

void OnConditionAchievedEventHandler(object sender, EventArgs e)
{
  myEvent.Set();
}

Недостатком этого является то, что я действительно не хочу засорять код моего производителя событиями, которые определены c на нужды потребителей.

2. Используйте Событие, накладные расходы на кодирование в зависимости от компромисса производительности

Если уже нет удобного события, которое можно подключить к (1), тогда производитель навсегда изменяется в соответствии с потребностями потребителей. Таким образом, очевидная естественная прогрессия - использовать что-то вроде INotifyPropertyChanged паттерна. Таким образом, не существует бесконечного расширения для производителей, и потребитель делает это:

void StateMachine_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if (e.Property == "State")
  {
    if (myStateMachine.State == State.TheStateThatIWant)
    {
      myEvent.Set();
    }
  }
}

Это похоже на выигрыш, потому что я часто использую систему NotifyPropertyChanged - это требуется для DataBinding, поэтому меньше кода для добавим, но, кажется, грязно, что мы слушаем каждое изменение в производителе, чтобы отфильтровать условие, которое нужно - конечно, есть лучший способ?!

3. Используйте задачу и опрос (тьфу)

Раскрутите задачу, которая проверяет состояние и спит, если условие не выполняется бесконечно или пока задача не будет отменена. Вызывающие абоненты, а затем дождитесь завершения задачи. Задача завершается, когда условие выполняется или отменяется.

Плюсы - делает для аккуратного кода, особенно при использовании лямбда-подхода Task.Run (() =>…), может использовать методы отмены задачи (токены, тайм-ауты et c), который часто также необходим. Против - опрос кажется грязным, кажется немного тяжелым, чтобы построить совершенно новую задачу для выполнения такой простой работы

4. Используйте задачу и ждите события

Лучше, чем опрос, верно? Но страдает от той же проблемы, что и в 1) и 2) необходимости соответствующего события, к которому необходимо подключиться, поэтому 2) (INotifyPropertyChanged) более распространено, чем 1). Поэтому реализация часто заканчивается ускорением задачи, ожиданием ManualResetEvent, прослушиванием PropertyChanged и фильтрацией изменений, срабатыванием события, возвратом из задачи.

5. Святой Грааль

Я не уверен на 100%, но что-то 1) легкое 2) позволяет указывать условие во время ожидания 3) не будет огромным ресурсным бременем, если 10 000 объектов ожидают в различных свойствах для достижения определенных значений 4) очищают, т.е. правильно распределяют ресурсы

MagicValueWaiter waitForValue = new MagicValueWaiter(MyStateMachine, nameof(State), (s) => (s > 4) && (s < 8));
await waitForInit.WaitAsync();

или

await ValueWaiter.WaitAsync(MyObject, nameof(MyPropertyorField), (s) => (s == States.Init);

Итак в основном, обобщенный c класс / метод для ожидания того, что данное свойство или поле данного объекта удовлетворяет определенному заданному условию в форме лямбда-выражения, возвращающего bool.

Этот подход может на первый взгляд предполагать однако метод опроса, если я заставляю MyObject соответствовать чему-то подобному, должен реализовывать INotifyPropertyChanged или некоторый пользовательский базовый класс для поддержки этого поведения, например ISupportValueWaiting, тогда мы могли бы подключиться к некоторому общему поведению, например, событиям в MyObject, и избежать опроса.

* 1063 Какие-нибудь очевидные решения, которые я пропускаю? Кто-нибудь получил какие-нибудь новые идеи о том, как это сделать? или комментарии по моему?

Ответы [ 2 ]

2 голосов
/ 17 апреля 2020

Что вы можете сделать, это использовать TaskCompletionSource и интерфейс INotifyPropertyChanged для завершения Task, как только какое-то условие на obj будет выполнено.

Итак:

public static class ConditionWaiter
{
    public static Task WaitForAsync<T>(this T obj, string PropertyName, Func<T, bool> pred)
        where T : INotifyPropertyChanged
    {
        obj = obj ?? throw new ArgumentNullException(nameof(obj));
        PropertyName = PropertyName ?? throw new ArgumentNullException(nameof(PropertyName));
        pred = pred ?? throw new ArgumentNullException(nameof(pred));

        var taskCompletionsource = new TaskCompletionSource<bool>();

        void handler(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == PropertyName && pred(obj))
            {
                obj.PropertyChanged -= handler;
                taskCompletionsource.SetResult(true);
            }
        }

        obj.PropertyChanged += handler;

        return taskCompletionsource.Task;
    }
}

И вы можете использовать его как:

await someValue.WaitForAsync(nameof(SomeType.SomeProperty), s => ...);
1 голос
/ 17 апреля 2020

То, что вы ищете, это реактивные расширения .

...