の概念に関する非常に基本的な質問がありConcurrentQueue
ました。キューは FIFO です。複数のスレッドがアクセスを開始した場合、FIFO をどのように保証しますか? Apple
、Oranges
、Lemon
、Peach
およびApricot
- をこの順序で追加したとします。最初TryTake
は を返す必要がありApple
ます。しかし、複数のスレッドが独自のTryTake
リクエストを出し始めるとどうなるでしょうか? Lemon
別のスレッドが戻る前に、あるスレッドが戻る可能性はありませApple
んか? キューが空になるまで、他のアイテムも返されると想定しています。しかし、これらのリターンは FIFO の基本原則を支配するのでしょうか?
1 に答える
自体の動作ConcurrentQueue
は常に FIFO になります。
からアイテムを「返す」スレッドについて話すとき、アイテムをデキューすることと、デキューされたものを観察できるようにする何らかの操作を実行するConcurrentQueue
ことの両方を含む操作について話しています。出力を印刷する場合でも、その項目を別のリストに追加する場合でも、検査するまで、どの項目がキューから取り出されたかは実際にはわかりません。
キュー自体は FIFO ですが、デキューされたアイテムの検査など、他のイベントが発生する順序を予測することはできません。項目は FIFO でデキューされますが、その順序で何がキューから出てくるかを観察できる場合とできない場合があります。異なるスレッドは、キューからアイテムを削除したのとまったく同じ順序でその検査または出力を実行しない場合があります。
言い換えれば、FIFO が発生する可能性がありますが、常にそのように見える場合とそうでない場合があります。ConcurrentQueue
アイテムが処理される正確な順序が重要な場合は、同時にから読み取りたくないでしょう。
これをテストすると (これから何かを書きます)、アイテムがほとんどの場合、正確な FIFO シーケンスで処理されることがわかりますが、そうでない場合もあります。
これがコンソールアプリです。それはするつもりです
ConcurrentQueue
シングルスレッドで 1 ~5000 の数字を挿入します。- 同時操作を実行して、これらの各アイテムをデキューし、別のアイテムに移動します
ConcurrentQueue
。これが「マルチスレッド コンシューマー」です。 - 2 番目のキュー (再びシングル スレッド) の項目を読み取り、順序が正しくない番号を報告します。
何度も実行しましたが、順序が間違っていることはありません。しかし、約 50% の確率で、いくつかの数字が順番どおりに表示されません。したがって、すべての数値が元の順序で処理されることを期待している場合、ほとんどの場合、ほとんどすべての数値で発生します。しかし、そうはなりません。正確なシーケンスを気にしないのであれば問題ありませんが、気にするとバグが発生し、予測できなくなります。
結論 - マルチスレッド操作の正確な順序に依存しないでください。
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
namespace ConcurrentQueueExperiment
{
class Program
{
static void Main(string[] args)
{
var inputQueue = new ConcurrentQueue<int>();
var outputQueue = new ConcurrentQueue<int>();
Enumerable.Range(1,5000).ToList().ForEach(inputQueue.Enqueue);
while (inputQueue.Any())
{
Task.Factory.StartNew(() =>
{
int dequeued;
if (inputQueue.TryDequeue(out dequeued))
{
outputQueue.Enqueue(dequeued);
}
});
}
int output = 0;
var previous = 0;
while (outputQueue.TryDequeue(out output))
{
if(output!=previous+1)
Console.WriteLine("Out of sequence: {0}, {1}", previous, output);
previous = output;
}
Console.WriteLine("Done!");
Console.ReadLine();
}
}
}