他の回答に追加するには、これが必要な理由bool
は次のとおりです (プロパティを単に切り替える代わりに)。
Signalling: でブロックされたスレッドはe.WaitOne()
signaledになり、そのうちの 1 つが続行されます。同期プリミティブを使用せずにこれを自分で行いたい場合は、何らかのポーリングを実装する必要があります。bool
「ブロッキング」スレッドは、 (または、たとえばint
)フィールドを定期的にポーリングして、フィールドが変更され、続行できるかどうかを確認する必要があります。他に何もないとしても、これは不必要に CPU サイクルを消費し、遅延が発生します (ポーリング間隔によって異なります)。
原子性: 複数のスレッドが待機している場合、1 つのみがブロック解除されることが保証されます。前述のポーリング ソリューションを使用して同じ動作を保証するには、ロックまたはアトミック命令 (Interlocked
クラスで見られるものなど) を使用し、可能なコンパイラおよびプロセッサ命令の並べ替え/メモリ バリアを十分に理解する必要があります。
複数のスレッドを同期する簡単な方法を探している場合、これは .NET で最も単純な (最も単純ではないにしても) ソリューションの 1 つです。プロデューサー/コンシューマー キューの作成は非常に簡単です。
// Simplified example. Check http://www.albahari.com/threading/part2.aspx
// for detailed explanations of this and other syncronizing constructs
private readonly AutoResetEvent _signal = new AutoResetEvent(false);
private readonly ConcurrentQueue<Something> _queue = new ConcurrentQueue<Something>();
// this method can be called by one or more threads simultaneously
// (although the order of enqueued items cannot be known if many threads are competing)
void ProduceItem(Something s)
{
_queue.Enqueue(s); // enqueue item for processing
_signal.Set(); // signal the consumer thread if it's waiting
}
// this loop should be running on a separate thread.
void ConsumerLoop()
{
while (!_ending)
{
// block until producer signals us
_signal.WaitOne();
// process whatever is enqueued
Something s = null;
while (!_ending && _concurrentQueue.TryDequeue(out s))
{
Process(s);
}
}
}
ただし、覚えておく必要があることの 1 つは、 を連続して複数回呼び出してSet
も、必ずしもWaitOne
複数回通知されるとは限らないということです。この例では、複数のプロデューサーがSet
メソッドを起動する可能性がありますが、コンテキストの切り替えが発生して継続するまでに数ミリ秒かかる場合があります。ConsumerLoop
つまり、Set
効果的に処理されるのは 1 つだけです。