AutoResetEvent process?
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.
精彩评论