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

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
        }
    }
}
Prix
  • 19,417
  • 15
  • 73
  • 132

2 Answers2

5

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I will check on that but I am still interested in knowing how the AutoResetEvent work for further understanding of it if I ever need it in the future again. – Prix Jul 19 '11 at 09:12
  • @Prix: Have edited to include that information. See the docs for AutoResetEvent for more information. – Jon Skeet Jul 19 '11 at 09:17
  • thanks for the update, In regards what you said at first, I am a bit confused as to wrap the concurrentqueue inside the blockingcollection, wouldnt it be easier to just use the blockingcolletion instead ? If you could I would appreciate some sample so I could understand better what you are saying ... – Prix Jul 19 '11 at 09:36
  • @Prix: BlockingCollection always wraps another collection - but if you just create a new BlockingCollection without specifying a collection to wrap, it'll use ConcurrentQueue automatically. I suggest you read the documentation for BlockingCollection, which gives examples of how to use it etc. – Jon Skeet Jul 19 '11 at 09:37
1

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.

Jon
  • 428,835
  • 81
  • 738
  • 806