是否有 .Net 类可以做什么ManualResetEvent.PulseAll()
(如果存在)?
我需要原子地释放一组等待同一信号的线程。(对于我的预期用途,我并不担心“线程踩踏”。)
您不能使用 aManualResetEvent
来执行此操作。例如,如果您这样做:
ManualResetEventSlim signal = new ManualResetEventSlim();
// ...
signal.Set();
signal.Reset();
然后根本不会释放等待信号的线程。
如果你在and调用Thread.Sleep(5)
之间放置一个,那么一些但不是所有的等待线程都被释放。将睡眠时间增加到 10 毫秒可以释放所有线程。(这是用 20 个线程测试的。)Set()
Reset()
显然,通过添加Thread.Sleep()
来完成这项工作是不可接受的。
然而,这很容易做到,Monitor.PulseAll()
我已经写了一个小类来做到这一点。(我编写一个类来执行此操作的原因是我们发现使用 Monitor 的逻辑虽然相当简单,但并不明显,因此值得拥有这样一个类来简化使用。)
我的问题很简单:.Net 中是否已经有一个类可以做到这一点?
作为参考,这是我的“ ManualResetEvent.PulseAll()
”等价物的基本版本:
public sealed class Signaller
{
public void PulseAll()
{
lock (_lock)
{
Monitor.PulseAll(_lock);
}
}
public void Wait()
{
Wait(Timeout.Infinite);
}
public bool Wait(int timeoutMilliseconds)
{
lock (_lock)
{
return Monitor.Wait(_lock, timeoutMilliseconds);
}
}
private readonly object _lock = new object();
}
这是一个示例程序,它演示了如果您在 Set() 和 Reset() 之间不休眠,则不会释放等待线程:
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);
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 threads to have started.
Thread.Sleep(100);
Console.WriteLine("Threads all started. Setting signal now.");
_signal.Set();
// Thread.Sleep(5); // With no sleep at all, NO threads receive the signal. Try commenting this line out.
_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)
{
_startCounter.Signal(); // Used so main thread knows when all threads have started.
_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;
}
}