2017-07-15 12 views
-1

毎回アイテムを取得したい(反復する)コンテナを作成したい場合は、でアイテムを返すようにしてください()。 本当に私はimpleamentを複数の種類の遅延(遅延+遅延、各項目を返す+遅れていくつかの項目を返す)をしたい。IEnumerableを実装するasync/awaitタスクを含める

しかし、私は次の例で考えを単純化します。 次のコードは動作しますが、await Task.Delay(...)を使用すると問題が発生します。私はコード内に記述します(コメントを読んでください)。

質問:

  • MyContainer objectを繰り返すスレッドのブロッキングを防ぐようGetEnumerator方法でそれを実装する方法はありますか?
  • このアイデア(興味深い解決策)を実現するあなたの興味深い方法は何ですか? :)

私は私が非表示にしたいとIEnumerable<T>に遅延機構を埋め込むことは明らかだと思います。

List<int>を超えて簡単にforeachを作成すると、高レベルのクラスのボディに遅延が含まれています。

エンベロープの遅延機構を持つコンテナを作成し、最後にMyContainerよりforeachをそのまま使用してください(遅いテストのように)。上記のコードの

class MyContainer : IEnumerable<int> 
{ 
    private readonly int _sec; 
    private readonly List<int> _items; 
    public MyContainer(List<int> items, int sec) 
    { 
     this._sec = sec; 
     this._items = items; 
    } 

    public IEnumerator<int> GetEnumerator() 
    { 
     for (int i = 0; i < _items.Count(); ++i) 
     { 
      //----------------------------------------------------------------- 
      // It works but block caller thread (E.g. block UI Thread) 
      Thread.Sleep(Sec*1000); 

      //----------------------------------------------------------------- 
      //Problem line: 
      //Because return type of current method should be async Task<...> 
      //And it seems «yield» has problem with Task methods 
      //await Task.Delay(_sec*1000); // <<< Error line 

      //----------------------------------------------------------------- 
      yield return _items[i]; 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

テストThread.Sleepと(await Task.Delayなし):

var myCnt=new MyContainer(new List<int>() {10,20,30,40,50}, 2); 
var sw=new Stopwatch(); 
sw.Start(); 

foreach (var item in myCnt) 
{ 
    Trace.WriteLine(sw.Elapsed); 
} 

sw.Stop(); 

// Result: 
// 00:00:02.0014343 
// 00:00:04.0034690 
// 00:00:06.0045362 
// 00:00:08.0056571 
// 00:00:10.0067891 
+1

あなたがしようとしていることは明確ではありません。あなたのイテレータに遅延を入れさせたいが、 'foreach'を遅らせたくないのですか?あなたが必要とする遅延/タイマーは、スタックの別の部分に実装する必要があるかもしれません。 –

+1

ところで、 'IAsyncEnumerable 'の実装を使用すると、async/awaitとenumerablesを混在させる方が簡単です:https://github.com/tyrotoxin/AsyncEnumerable –

+0

@NateBarbettini、 'IAsyncEnumerable 'リンクは面白いです!その答えの例としてここにあなたのコメントを書くことができますか?私は、この質問ページで他の人たちを助けてくれると思います。もし彼らが私のように 'IEnumerable 'と' Task'sをミックスしたいのであれば。 リンク: _(a)他の非同期イベントへの依存性のために要素の作成に時間がかかる要素プロバイダを作成します(例:(処理をワーカースレッドでスケジューリングしている)。_ – RAM

答えて

0

これは概念的にはあなたの道のほとんどを得るべき、とあなたはそれを微調整することができます。

考えられるのは、ブロックしていないということですが、生成された値のそれぞれは、制御可能な遅延で待つことができます。

public static IEnumerable<Task<T>> EnumerateWithDelay<T>(Func<int, T> generator, Func<int, T, bool> limiter, TimeSpan delay) 
{ 
    var idx = 0; 
    while (true) 
    { 
     var item = generator(idx); 
     if (!limiter(idx, item)) yield break; 
     yield return Task.Delay(delay).ContinueWith(_ => item); 
     idx++; 
    } 
} 

public async Task A() 
{ 
    foreach (var itemTask in EnumerateWithDelay(idx => idx, (idx, val) => idx < 10, TimeSpan.FromSeconds(0.5))) 
    { 
     // I'll take .5 seconds 
     var number = await itemTask; 
    } 
} 

少し微調整して、遅延が上がるまでジェネレータ関数を呼び出さないようにすることができます。

関連する問題