2014-01-07 4 views
6

C#では、BlockingCollectionがバックグラウンドスレッドによってクリアされるまで待つことが可能かどうか、時間がかかり過ぎるとタイムアウトが発生するのではないかと思います。BlockingCollectionキューがバックグラウンドスレッドによってクリアされるまで待ちますが、時間がかかりすぎるとタイムアウトが発生しますか?

現時点で私が持っている一時的なコードはやや洗練として私を打つ(それはThread.Sleepを使用することをお勧めしたとき以来?):

while (_blockingCollection.Count > 0 || !_blockingCollection.IsAddingCompleted) 
{ 
    Thread.Sleep(TimeSpan.FromMilliseconds(20)); 
    // [extra code to break if it takes too long] 
} 
+2

私はあなただけ()BlockingCollection.CompleteAddingを呼び出した後にこれをやっていると仮定 - 正しいですか?とにかく、実際にあなたが持っているものよりも良い方法はありません。 –

+0

@Matthew Watson。いい視点ね;キューに追加されていないキューアイテムがないことを確認するために追加の用語を追加しました。また、バックグラウンドスレッドがキューをクリアしているということを述べるための質問を明確にしました。 – Contango

+1

「待つ」とはどういう意味ですか? TryTakeを使用している場合は、実際にタイムアウトを指定することができます。列挙子を使用している場合はさらに制限されますが、原則としてTryTakeを使用する列挙子ラッパーを作成できます。 – Holstebroe

答えて

4

何があなたを消費スレッドでこのような何かを書く場合:

var timeout = TimeSpan.FromMilliseconds(10000); 
T item; 
while (_blockingCollection.TryTake(out item, timeout)) 
{ 
    // do something with item 
} 
// If we end here. Either we have a timeout or we are out of items. 
if (!_blockingCollection.IsAddingCompleted) 
    throw MyTimeoutException(); 
+0

このパターンは非常にうまく機能し、私はそれを専門的に何年も使用してきました。 – Contango

2

あなたは、イベントがときに設定できるように再設計することができる場合コレクションが空である、あなたは、コレクションが空であるのを待つために待機ハンドルを使用することができます。

static void Main(string[] args) 
{ 
    BlockingCollection<int> bc = new BlockingCollection<int>(); 
    ManualResetEvent emptyEvent = new ManualResetEvent(false); 
    Thread newThread = new Thread(() => BackgroundProcessing(bc, emptyEvent)); 
    newThread.Start(); 
    //wait for the collection to be empty or the timeout to be reached. 
    emptyEvent.WaitOne(1000); 

} 
static void BackgroundProcessing(BlockingCollection<int> collection, ManualResetEvent emptyEvent) 
{ 
    while (collection.Count > 0 || !collection.IsAddingCompleted) 
    { 
     int value = collection.Take(); 
     Thread.Sleep(100); 
    } 
    emptyEvent.Set(); 
} 
6

あなたは、キューが空であるかを決定するために消費するスレッドでGetConsumingEnumerable()foreachを使用して、メインスレッドはキューが空であるかどうかを確認することができManualResetEventを設定することができます。 GetConsumingEnumerable()は空のキューで終了する前にCompleteAdding()が呼び出されたかどうかをチェックする列挙子を返します。

サンプルコード:

using System; 
using System.Collections.Concurrent; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Demo 
{ 
    internal class Program 
    { 
     private void run() 
     { 
      Task.Run(new Action(producer)); 
      Task.Run(new Action(consumer)); 

      while (!_empty.WaitOne(1000)) 
       Console.WriteLine("Waiting for queue to empty"); 

      Console.WriteLine("Queue emptied."); 
     } 

     private void producer() 
     { 
      for (int i = 0; i < 20; ++i) 
      { 
       _queue.Add(i); 
       Console.WriteLine("Produced " + i); 
       Thread.Sleep(100); 
      } 

      _queue.CompleteAdding(); 
     } 

     private void consumer() 
     { 
      foreach (int n in _queue.GetConsumingEnumerable()) 
      { 
       Console.WriteLine("Consumed " + n); 
       Thread.Sleep(200); 
      } 

      _empty.Set(); 
     } 

     private static void Main() 
     { 
      new Program().run(); 
     } 

     private BlockingCollection<int> _queue = new BlockingCollection<int>(); 

     private ManualResetEvent _empty = new ManualResetEvent(false); 
    } 
} 
関連する問題