タイマーの共同イテレーター

前の記事で、 DispatcherTimerからのイベントで実行されるイテレーター関数の例を挙げました。 このアイデアをさらに発展させることは私にとって興味深いことでした。 独立したタイマーを備えたこのようないくつかの機能は、協調的なマルチタスクを備えたシステムを形成します。 以下では、このような関数の共同イテレーターを呼び出します。



共同イテレーターはプログラムのメインスレッドで実行されるため、相互の通信は、

ユーザーインターフェイス要素を使用すると、スレッド間の相互作用を使用する必要がありません。



async / awaitのように 、コイテレータを使用すると、アクションのシーケンシャル実行のロジックを予期して記述できますが、呼び出しの前後だけでなくオンにも有効になるデコレータを使用して動作を大幅に変更できる点が異なりますすべての反復。



進行状況バーと中断の可能性を備えた長時間実行アクションの従来のタスクを実行します。 標準的な解決策は次の方法です。



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; } } }
      
      








All Articles