AutoResetEvent进程?

时间:2011-07-19 09:02:21

标签: c# autoresetevent concurrent-collections

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
        }
    }
}

2 个答案:

答案 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来验证上述信息。