我正在MSDN上阅读AutoResetEvent文档,并且警告有点困扰我......
“重要: 无法保证每次调用Set方法都会释放一个线程。如果两个调用太靠近,那么第二次调用在线程释放之前发生,则只释放一个线程。就好像第二次通话没有发生一样。此外,如果在没有线程等待并且已经发出AutoResetEvent信号的情况下调用Set,则该调用无效。“
但是这个警告基本上杀死了拥有这种线程同步技术的原因。例如,我有一个列表,可以保留工作。并且只有一个生产者将添加作业到列表中。我有消费者(不止一个),等着从列表中得到这份工作......就像这样..
制片:
void AddJob(Job j)
{
lock(qLock)
{
jobQ.Enqueue(j);
}
newJobEvent.Set(); // newJobEvent is AutoResetEvent
}
消费
void Run()
{
while(canRun)
{
newJobEvent.WaitOne();
IJob job = null;
lock(qLock)
{
job = jobQ.Dequeue();
}
// process job
}
}
如果上述警告为真,那么如果我很快将两个作业排队,那么只有一个线程会接受这个工作,不是吗?我假设Set将是原子的,即它执行以下操作:
所以我基本上对MSDN中的警告感到困惑。这是一个有效的警告吗?
答案 0 :(得分:2)
即使警告不正确且Set为原子,为什么要在这里使用AutoResetEvent?假设您有一些生产者排队等待3个事件,并且有一个消费者。在处理完第二个作业之后,消费者会阻止并且永远不会处理第三个作业。
我会使用ReaderWriterLockSlim进行此类同步。基本上,您需要多个生产者才能拥有写入锁,但是您不希望消费者在只读取队列大小的情况下长时间锁定生产者。
答案 1 :(得分:2)
MSDN上的消息确实是有效的消息。内部发生的事情是这样的:
请注意,内部逻辑不同步,因为线程B不等待线程A继续其业务。你可以通过引入一个临时的ManualResetEvent使这个同步,线程A必须在它继续工作并且线程B必须等待时发出信号。由于Windows线程模型的内部工作,默认情况下不会这样做。我想文档是误导性的,但正确的说Set方法只释放一个或多个等待线程。
或者我建议你看一下.NET 4.0中引入的BCL的System.Collections.Concurrent命名空间中的BlockingCollection类,它正是你想要做的事情