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或事件在它回击_queueNotifier.WaitOne()
或它如何正常工作时自行回归为假?
我应该像下面的例子那样使用内部,或两种方式都很好/相等吗?
while (_socket.Connected)
{
_queueNotifier.WaitOne();
while (!_queue.IsEmpty)
{
Data data;
if (_queue.TryDequeue(out data))
{
//handle the data
}
}
}
答案 0 :(得分:4)
如果你在.NET 4中使用ConcurrentQueue
,最好避免完全自己处理AutoResetEvent
。相反,创建一个BlockingCollection
来包装ConcurrentQueue
并使用它 - 它可以完成您需要的一切。 (如果您只是使用无参数构造函数创建BlockingCollection
,那么无论如何它都会为您创建ConcurrentQueue
。)
编辑:如果你真的想继续使用AutoResetEvent
,那么WaitOne
会自动(并原子地)重置事件 - 这是AutoResetEvent
的“自动”部分。将其与未重置事件的ManualResetEvent
进行比较。
答案 1 :(得分:1)
当您执行_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来验证上述信息。