是否有.Net类可以执行的ManualResetEvent.PulseAll()
操作(如果存在)?
我需要原子地释放一组等待相同信号的线程.(我并不担心我的预期用法会出现"线程踩踏事件".)
你不能用a ManualResetEvent
来做这件事.例如,如果你这样做:
ManualResetEventSlim signal = new ManualResetEventSlim();
// ...
signal.Set();
signal.Reset();
Run Code Online (Sandbox Code Playgroud)
然后根本没有释放等待信号的线程.
如果你Thread.Sleep(5)
在Set()
和Reset()
调用之间放置一个,那么一些但不是所有等待的线程都被释放.将睡眠时间增加到10毫秒可以释放所有线程.(这是用20个线程测试的.)
显然,加入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();
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例程序,演示如果您没有在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;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用Barrier对象。它允许运行未指定数量的任务,然后等待所有其他任务到达该点。
如果您不知道哪些代码块中的哪些任务将开始作为特定的工作单元工作,则可以以类似于 Go 中的 WaitGroup 的方式使用它。