2011-07-19 24 views
1
private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>(); 
private AutoResetEvent _queueNotifier = new AutoResetEvent(false); 

public void MoreData(Data example) 
{ 
    _queue.Enqueue(example); 
    _queueNotifier.Set(); 
} 

private void _SimpleThreadWorker() 
{ 
    while (_socket.Connected) 
    { 
     _queueNotifier.WaitOne(); 
     Data data; 
     if (_queue.TryDequeue(out data)) 
     { 
      //handle the data 
     } 
    } 
} 

当我拥有它时,我是否必须将该事件设置为假当它返回_queueNotifier.WaitOne()或它是如何工作的时候,出列还是事件回到假?AutoResetEvent进程?

我是否应该像下面的例子那样使用inner,或者两种方式都好吗?

while (_socket.Connected) 
{ 
    _queueNotifier.WaitOne(); 
    while (!_queue.IsEmpty) 
    { 
     Data data; 
     if (_queue.TryDequeue(out data)) 
     { 
      //handle the data 
     } 
    } 
} 

回答

4

如果您使用ConcurrentQueue从.NET 4中,最好避免做AutoResetEvent处理自己完全。相反,创建一个BlockingCollection来包装ConcurrentQueue,并使用它 - 它可以满足您的所有需求。 (如果你只是使用参数的构造函数创建一个BlockingCollection,它会反正为你创建一个ConcurrentQueue

编辑:如果你真的想仍然使用AutoResetEvent,然后WaitOne将自动(并自动)重置事件 - 这是AutoResetEvent的“自动”部分。与ManualResetEvent比较,其中不是重置事件。

+0

我会检查一下,但我仍然有兴趣了解AutoResetEvent如何在未来再次需要时进一步了解它。 – Prix

+0

@Prix:已编辑以包含该信息。有关更多信息,请参阅AutoResetEvent的文档。 –

+0

感谢您的更新,至于你刚才说的话,我有点困惑,因为将concurrentqueue包装在blockingcollection中,而不是仅仅使用blockingcolletion更容易?如果你能,我会欣​​赏一些样本,所以我可以更好地理解你在说什么...... – Prix

1

当你做_queueNotifier.Set()事件变成信号。当被通知的事件和_queueNotifier.WaitOne()从其他线程调用,两件事情(即在内核模式下)同时发生:

  • 事件变得unsignaled(因为它是自动复位)
  • 线程调用WaitOne是畅通无阻

所以你不必自己明确设置事件状态。

但是,正如Jon所说,如果您对共享变量所做的唯一操作是从队列中推拉项目,只需使用BlockingCollection更方便。

如果你正在访问多个共享变量,那么围绕它可能会有一个单独的线程同步机制(你自己的)。

而且,在我看来,如果你要使用自己的代码,将是更好的做这样的事情:

public void MoreData(Data example) 
{ 
    var queueWasEmpty = _queue.IsEmpty; 
    _queue.Enqueue(example); 
    if (queueWasEmpty) { 
     _queueNotifier.Set(); 
    } 
} 

private void _SimpleThreadWorker() 
{ 
    while (_socket.Connected) 
    { 
     _queueNotifier.WaitOne(); 
     Data data; 
     while(!queue.IsEmpty) { 
      if (_queue.TryDequeue(out data)) 
      { 
       //handle the data 
      } 
     } 
    } 
} 

这样你就不必去到内核模式(设置事件),只要消费者功能繁忙时项目被添加到队列中。您还可以避免在消费者函数的每次迭代中进入内核模式。

对于后者来说,避免上下文切换的确是以增加_queue.IsEmpty测试为代价的(其中一个糟糕的实现可能会做上下文切换);然而,这可能是作为互锁比较交换操作实现的,不需要进入内核模式。

声明:我没有检查源代码或IL,以验证上述信息。

+0

谢谢,这是一个很好的解释:) – Prix