ManualResetEventSlim:.Set()を呼び出した直後に.Reset()を呼び出しても、待機中のスレッドは解放されません
(注:これはManualResetEvent
、だけでなく、でも発生しManualResetEventSlim
ます。)
以下のコードをリリースモードとデバッグモードの両方で試しました。クアッドコアプロセッサで実行されているWindows764ビットで.Net4を使用して32ビットビルドとして実行しています。Visual Studio 2012からコンパイルしました(したがって、.Net 4.5がインストールされています)。
システムで実行したときの出力は次のとおりです。
Waiting for 20 threads to start
Thread 1 started.
Thread 2 started.
Thread 3 started.
Thread 4 started.
Thread 0 started.
Thread 7 started.
Thread 6 started.
Thread 5 started.
Thread 8 started.
Thread 9 started.
Thread 10 started.
Thread 11 started.
Thread 12 started.
Thread 13 started.
Thread 14 started.
Thread 15 started.
Thread 16 started.
Thread 17 started.
Thread 18 started.
Thread 19 started.
Threads all started. Setting signal now.
0/20 threads received the signal.
したがって、イベントを設定してすぐにリセットしても、単一のスレッドは解放されませんでした。Thread.Sleep()のコメントを外すと、それらはすべて解放されます。
これはやや予想外のようです。
誰か説明がありますか?
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
public static class Program
{
private static void Main(string[] args)
{
_startCounter = new CountdownEvent(NUM_THREADS); // Used to count #started threads.
for (int i = 0; i < NUM_THREADS; ++i)
{
int id = i;
Task.Factory.StartNew(() => test(id));
}
Console.WriteLine("Waiting for " + NUM_THREADS + " threads to start");
_startCounter.Wait(); // Wait for all the threads to have called _startCounter.Signal()
Thread.Sleep(100); // Just a little extra delay. Not really needed.
Console.WriteLine("Threads all started. Setting signal now.");
_signal.Set();
// Thread.Sleep(50); // With no sleep at all, NO threads receive the signal.
_signal.Reset();
Thread.Sleep(1000);
Console.WriteLine("\n{0}/{1} threads received the signal.\n\n", _signalledCount, NUM_THREADS);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
private static void test(int id)
{
Console.WriteLine("Thread " + id + " started.");
_startCounter.Signal();
_signal.Wait();
Interlocked.Increment(ref _signalledCount);
Console.WriteLine("Task " + id + " received the signal.");
}
private const int NUM_THREADS = 20;
private static readonly ManualResetEventSlim _signal = new ManualResetEventSlim();
private static CountdownEvent _startCounter;
private static int _signalledCount;
}
}
注:この質問も同様の問題を引き起こしますが、答えがないようです(はい、これが発生する可能性があることを確認する以外は)。
ManualResetEventがすべての待機中のスレッドを一貫して解放しない問題
[編集]
Ian Griffithsが以下で指摘しているように、答えは、使用されている基盤となるWindowsAPIがこれをサポートするように設計されていないということです。
ManualResetEventSlim.Set()に関するMicrosoftのドキュメントに、誤って記載されているのは残念です。
イベントの状態をsignaledに設定します。これにより、イベントを待機している1つ以上のスレッドが続行できるようになります。
明らかに、「1つ以上」は「0以上」である必要があります。