开发者

AutoResetEvent process?

开发者 https://www.devze.com 2023-03-21 01:57 出处:网络
private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>(); private AutoResetEvent _queueNotifier = new AutoResetEvent(false);
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
        }
    }
}

Do I have to set the event to false once I have it Dequeue or the event goes back to false on it's own when it hits back _queueNotifier.WaitOne() or how it works exactly ?

Should I use a inner while like the below example instead or both ways are 开发者_高级运维just fine/equal ?

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


If you're using ConcurrentQueue from .NET 4, it's best to avoid doing the AutoResetEvent handling yourself entirely. Instead, create a BlockingCollection to wrap the ConcurrentQueue and just use that - it does everything you need. (If you just create a BlockingCollection using the parameterless constructor, it'll create a ConcurrentQueue for you anyway.)

EDIT: If you really want to still use AutoResetEvent, then WaitOne will automatically (and atomically) reset the event - that's the "Auto" part of AutoResetEvent. Compare this with ManualResetEvent which doesn't reset the event.


When you do _queueNotifier.Set() the event becomes signaled. When the event is signaled and _queueNotifier.WaitOne() is called from the other thread, two things happen simultaneously (i.e. in kernel mode):

  • The event becomes unsignaled (since it's auto-reset)
  • The thread calling WaitOne is unblocked

So you don't have to explicitly set the event status yourself.

However, as Jon says, if the only thing you are doing with your shared variables is to push and pull items from the queue, simply using a BlockingCollection is more convenient.

If you are accessing multiple shared variables then it might make sense to have a single thread sync mechanism (your own) around that.

Also, it seems to me that if you are going to use your own code it would be better to do something like this:

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

This way you won't have to go into kernel mode (to set the event) whenever items get added to the queue while the consumer function is busy. You also avoid going into kernel mode in each iteration of the consumer function.

It's true that regarding the latter, avoiding the context switch comes at the cost of adding the _queue.IsEmpty test (wherein a bad implementation might do a context switch anyway); however, that one is probably implemented as an interlocked compare exchange operation which doesn't require going into kernel mode.

Disclaimer: I have not checked source code or IL to verify the above information.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号