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

我是否必须将事件设置为 false 一旦我拥有它 Dequeue 或事件在它回击时自行返回 false_queueNotifier.WaitOne()或它是如何工作的?

我应该像下面的示例那样使用内部 while 还是两种方式都很好/相等?

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

2 回答 2

4

如果您ConcurrentQueue从 .NET 4 开始使用,最好避免AutoResetEvent完全自己进行处理。相反,创建一个BlockingCollection来包装ConcurrentQueue并使用它 - 它可以满足您的一切需求。(如果您只是BlockingCollection使用无参数构造函数创建一个ConcurrentQueue,无论如何它都会为您创建一个。)

编辑:如果您真的想仍然使用AutoResetEvent,那么WaitOne将自动(并且以原子方式)重置事件 - 这是AutoResetEvent. 将此与ManualResetEvent重置事件的进行比较。

于 2011-07-19T09:05:18.883 回答
1

当你这样做时_queueNotifier.Set(),事件就会发出信号。当事件发出信号并_queueNotifier.WaitOne()从另一个线程调用时,会同时发生两件事(即在内核模式下):

  • 事件变为未发出信号(因为它是自动重置的)
  • 线程调用WaitOne被解除阻塞

因此,您不必自己显式设置事件状态。

但是,正如 Jon 所说,如果您对共享变量所做的唯一事情是从队列中推送和拉取项目,那么简单地使用 aBlockingCollection会更方便。

如果您正在访问多个共享变量,那么围绕它使用单线程同步机制(您自己的)可能是有意义的。

另外,在我看来,如果您要使用自己的代码,最好执行以下操作:

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

于 2011-07-19T09:13:35.830 回答