Коитераторы По Таймерам

В предыдущая статья Я привел пример функции-итератора, выполняемой по событию из ДиспетчерТаймер .

Мне стало интересно развивать эту идею дальше.

Некоторые из этих функций с независимыми таймерами образуют совместную многозадачную систему.

В дальнейшем мы будем называть такие функции коитераторами.

Коитераторы выполняются в основном потоке программы, поэтому их общение похоже друг на друга.

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

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

Возьмем традиционную задачу длительного действия с отображением прогресса и возможностью прерывания.

Стандартным решением будет этот метод:

  
  
  
  
  
  
  
  
  
   

public struct ProgressInfo { public string Message; public int Current, Total; } async Task Foo(IProgress<ProgressInfo> progress, CancellationToken ct)

Доверим прерывание вызывающему коду, а информацию о прогрессе вернем в результате итерации.



public static IEnumerable<object> Process<T>(this ICollection<T> collection, Action<T> action) { ProgressInfo info = new ProgressInfo() { Total = collection.Count }; foreach (var item in collection) { action(item); info.Current++; info.Message = string.Format("Processed {0:d} from {1:d}", info.Current, info.Total); yield return info; } yield break; }

Рассмотрите возможность вызова соитератора из соитератора.



IEnumerable<object> Foo() { yield break; } IEnumerable<object> Bar() { yield break; } IEnumerable<object> Baz() { foreach (var a in Foo()) yield return a; foreach (var b in Bar()) yield return b; yield break; }

Давайте примем соглашение, что в таких случаях мы будем возвращать итератор напрямую:

IEnumerable<object> Baz() { yield return Foo(); yield return Bar(); yield break; }

А о разворачивании позаботится следующий метод:

public static IEnumerable<object> Flat(this IEnumerable<object> cot) { foreach (var a in cot) { var ea = a as IEnumerable<object>; if (ea==null) yield return a; else { foreach (var b in ea.Flat()) yield return b; } } yield break; }

Теперь вы можете написать несколько декораторов.

Беги молча:

public static void Execute(this IEnumerable<object> cot) { foreach (var a in cot.Flat()) { } }

Выполнить с таймаутом:

public class ValueOf<T> { public T Value; } public static IEnumerable<object> Timeout(this IEnumerable<object> cot, TimeSpan duration, ValueOf<bool> timeout) { var limit = DateTimeOffset.Now+duration; foreach (var a in cot.Flat()) { if (DateTimeOffset.Now>limit) { timeout.Value = true; break; } yield return a; } yield break; }

Выполняйте меньше/чаще:

public static IEnumerable<object> Rate(this IEnumerable<object> cot, double rate) { double summ = 0.001; foreach (var a in cot.Flat()) { summ += 1.0; while (summ>rate) { summ -= rate; yield return a; } } yield break; }

Дождитесь условий:

public static IEnumerable<object> Wait(Func<bool> condition) { while (!condition()) yield return null; yield break; }

Наконец, код объекта, выполняющего описанные выше соитераторы, имеет вид что не так уж и интересно.



public class TimerThread { bool _IsCancelled, _IsCompleted; DispatcherTimer Timer; IEnumerator<object> Enumerator; public event Action<ProgressInfo> Progress; public TimerThread(IEnumerable<object> cot, double interval) { Enumerator = cot.Flat().

GetEnumerator(); Timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(interval) }; Timer.Tick += Timer_Tick; } void Timer_Tick(object sender, EventArgs ea) { if (!Enumerator.MoveNext()) { _IsCompleted = true; Timer.IsEnabled = false; } else if (Enumerator.Current is ProgressInfo) { if (Progress!=null) Progress((ProgressInfo)Enumerator.Current); } } public bool IsEnabled { get { return Timer.IsEnabled; } set { if (_IsCancelled || _IsCompleted) return; Timer.IsEnabled = value; } } public bool IsCancelled { get { return _IsCancelled; } set { if (!value) return; _IsCancelled = true; Timer.IsEnabled = false; } } public bool IsCompleted { get { return _IsCompleted; } } }

Теги: #C++ #wpf #.

NET #C++ #Разработка Windows

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.