web-dev-qa-db-ja.com

TryDequeueをwhileループに入れても安全ですか?

以前に並行キューを使用したことがありません。

Whileループで、以下のようにTryDequeueを使用しても問題ありませんか?これは永遠に行き詰まらないのでしょうか?

var cq = new ConcurrentQueue<string>();
cq.Enqueue("test");

string retValue;

while(!cq.TryDequeue(out retValue))
{
    // Maybe sleep?
}

//Do rest of code
17
ManInMoon

はい、ドキュメントによると安全ですが、推奨される設計ではありません。

最初の呼び出しTryDequeueでキューが空であり、その時点以降に他のスレッドがキュー内のデータをプッシュしなかった場合は、「永久にスタック」する可能性があります(ただし、N回の試行後またはタイムアウト後にwhileを中断できます)。

ConcurrentQueueは IsEmpty メンバーを提供して、キューにアイテムがあるかどうかを確認します。それをチェックすることは、TryDequeue呼び出しをループするよりもはるかに効率的です(特に、キューが通常空の場合)。

mightがしたいことは次のとおりです。

while(cq.IsEmpty())
{
    // Maybe sleep / wait / ...
}

if(cq.TryDequeue(out retValue))
{
...
}

編集:この最後の呼び出しがfalseを返す場合:別のyourスレッドがアイテムをデキューしました。他のスレッドがない場合は安全ですが、ある場合はwhile(TryDequeue)を使用してください。

12
quantdev

取り出されたアイテムがあるまでループは実際には終了せず、キューに取り出されるアイテムがある場合、ループは最終的に終了するという意味で安全です。キューが別のスレッドによって空にされ、それ以上アイテムが追加されない場合、もちろんループは終了しません。

それ以外にも、ビジーループがあります。これは事実上常に避けられるべきです。追加のアイテムを求めてキューを絶えずポーリングし、CPUの時間と労力を無駄にしてプロセスを終了させるか、スリープ状態になり、キューにアイテムが追加されるとすぐに実際には使用しなくなります(それでも無駄になっています)。 someキューをポーリングするだけのコンテキストスイッチの時間/労力)。

代わりにあなたがすべきことは、「私がとるべきアイテムがあるまで待つ」という立場に自分がいることに気づいたら、BlockingCollectionを使用することです。 具体的には、さまざまなタイプの並行コレクションをラップし、取得可能なアイテムがあるまでブロックするように設計されています。これにより、コードをqueue.Take()に変更し、記述が簡単になり、意味的に作業内容を記述し、明確に正確になり、著しく効果的になり、完全に安全になります。

15
Servy