2012-02-29 42 views
2

我在MSDN上阅读的AutoResetEvent文档和以下警告有点困扰我。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 
    } 
} 

如果上面的警告是真实的,那么如果我很快排队两个工作,只有一个线程会拿起工作,不是吗?我是假设下集将是原子,那就是它具有以下功能:

  1. 设置事件
  2. 如果线程都在等待,挑一个线程来唤醒
  3. 重置事件
  4. 运行选定的线程。

所以我基本上对MSDN中的警告感到困惑。这是一个有效的警告吗?

+0

是的,这个代码将只工作正确的意外。不要发明这个轮子,谷歌的“C#生产者消费者队列”大量的样品。 – 2012-02-29 05:24:21

+0

我发现大多数生产者消费者搜索,使用等待/信号方法。这种方法的问题在于,当我想让工人辞职时,我不得不提交虚假工作。但是如果我可以使用事件,我可以让工作人员等待两个事件。一个工作和其他关闭。那感觉更清洁.. – cgcoder 2012-02-29 19:49:44

回答

2

即使警告不是真,Set是原子,为什么你会在这里使用AutoResetEvent?假设你有一些生产者排队排队3个事件,并且有一个消费者。处理第二份工作后,消费者会阻止并且不会处理第三份。

我会使用ReaderWriterLockSlim这种类型的同步。基本上,您需要多个生产者才能拥有写入锁定,但您不希望消费者在只读取队列大小的情况下长时间锁定生产者。

2

MSDN上的消息确实是一个有效的消息。什么是内部发生的事情是这样的:

  1. 线程B组事件的事件线程A等待
  2. [如果线程A是自旋锁]
    1. [是]条条的检测事件设置,取消设置并恢复工作
    2. [no]该事件将告诉线程A唤醒,一旦唤醒,线程A将取消该事件的恢复工作。

注意,内部逻辑不同步,因为线程B不等待线程A继续其业务。您可以通过引入一个临时的ManualResetEvent来实现同步,该临时的ManualResetEvent线程A在继续工作并且线程B必须等待时必须发出信号。由于windows线程模型的内部工作,这不是默认完成的。我猜文档是误导性的,但是正确的说Set方法只能释放一个或多个等待线程。

另外我建议你看在BCL的System.Collections.Concurrent命名空间中的类BlockingCollection介绍在.NET 4.0中这不正是你正在尝试做的

相关问题