ManualResetEventSlim:立即调用 .Set() 后跟 .Reset() 不会释放任何等待线程
(注意:这也发生在ManualResetEvent
,而不仅仅是ManualResetEventSlim
。)
我在发布和调试模式下都尝试了下面的代码。我在四核处理器上运行的 Windows 7 64 位上使用 .Net 4 将其作为 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 在下面指出的那样,答案是使用的底层 Windows API 并非旨在支持这一点。
不幸的是,ManualResetEventSlim.Set() 的 Microsoft 文档错误地指出它
将事件的状态设置为已发出信号,这允许一个或多个等待事件的线程继续进行。
显然“一个或多个”应该是“零个或多个”。