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
}
}
}
我是否必须将事件设置为false一旦我有它Dequeue或事件返回false当它返回_queueNotifier.WaitOne()
或它是如何工作的?
我应该像下面的例子一样使用内部while,还是两种方法都很好/相等?
while (_socket.Connected)
{
_queueNotifier.WaitOne();
while (!_queue.IsEmpty)
{
Data data;
if (_queue.TryDequeue(out data))
{
//handle the data
}
}
}
如果您正在使用。net 4中的ConcurrentQueue
,最好完全避免自己处理AutoResetEvent
。相反,创建一个BlockingCollection
来包装ConcurrentQueue
并使用它-它可以完成您需要的一切。(如果您只是使用无参数构造函数创建一个BlockingCollection
,它将为您创建一个ConcurrentQueue
。)
编辑:如果你真的想仍然使用AutoResetEvent
,那么WaitOne
将自动(和自动)重置事件-这是AutoResetEvent
的"自动"部分。与ManualResetEvent
相比,不重置事件
当您执行_queueNotifier.Set()
时,事件变得有信号。当事件发出信号并且从另一个线程调用_queueNotifier.WaitOne()
时,两件事同时发生(即在内核模式下):
- 事件变为无信号(因为它是自动复位的)
- 调用
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来验证上述信息。