Reputation: 2758
I am writing a multi threaded application and I have concerns with 2 threads access a queue
Thread 1 puts items in a queue for processing Thread 2 removes items from the queue to process
Thread 1 runs once a minute due to the nature of the data it is pulling. Thread 2 is always running, it removes an item from the queue and sleeps for 100ms. I have to do this to ensure that I don't overwhelm a service it calls when it dequeues an item.
I assume both threads should place a lock on the queue when adding or removing items from it. Are there any further considerations? For instance say Thread 1 has a lock and Thread 2 tries to access it. Does Thread 2 simply know to wait and resume once the lock is removed?
Would it be preferable to use a ConcurrentQueue and just TryDequeue and if it fails just go about its 100 ms sleep?
Thanks in advance
Upvotes: 2
Views: 2306
Reputation: 52430
It's even easier if you use a BlockingCollection<T>
like I do in NuGet's VS console dispatcher for my PostKey/WaitKey implementation. The consuming thread calls Take(...)
which will block until another thread calls Add(...)
. There is no need to poll. Additionally, you may wish to pass a cancellation token to the Take
method so another thread can stop the consumer thread if it's currently waiting for an Add
that will never come. Here are the relevant methods:
private readonly BlockingCollection<VsKeyInfo> _keyBuffer =
new BlockingCollection<VsKeyInfo>();
private CancellationTokenSource _cancelWaitKeySource;
// place a key into buffer
public void PostKey(VsKeyInfo key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
_keyBuffer.Add(key);
}
// signal thread waiting on a key to exit Take
public void CancelWaitKey()
{
if (_isExecutingReadKey && !_cancelWaitKeySource.IsCancellationRequested)
{
_cancelWaitKeySource.Cancel();
}
}
// wait for a key to be placed on buffer
public VsKeyInfo WaitKey()
{
try
{
// raise the StartWaitingKey event on main thread
RaiseEventSafe(StartWaitingKey);
// set/reset the cancellation token
_cancelWaitKeySource = new CancellationTokenSource();
_isExecutingReadKey = true;
// blocking call
VsKeyInfo key = _keyBuffer.Take(_cancelWaitKeySource.Token);
return key;
}
catch (OperationCanceledException)
{
return null;
}
finally
{
_isExecutingReadKey = false;
}
}
See http://nuget.codeplex.com/SourceControl/changeset/view/45e353aca7f4#src%2fVsConsole%2fConsole%2fConsole%2fConsoleDispatcher.cs for more details.
Upvotes: 8
Reputation: 24857
If you're going to sleep for 100ms anyway, you can pretty much implement the queue any way you want as long as it's thread-safe. A simple queue with a lock/mutex, a ConcurrentQueue, they're all fine.
'For instance say Thread 1 has a lock and Thread 2 tries to access it. Does Thread 2 simply know to wait and resume once the lock is removed' - Thread 2 waits until the lock is released, yes.
Upvotes: 1
Reputation: 7584
ConcurrentQueue
is thread safe so you wouldn't need to lock, just call TryDequeue
and you will be fine with no other special coding. Do not iterate it though, per the documentation, it will snapshot on iteration (unless that was what you wanted): http://msdn.microsoft.com/en-us/library/dd287144.aspx
The enumeration represents a moment-in-time snapshot of the contents of the queue. It does not reflect any updates to the collection after GetEnumerator was called. The enumerator is safe to use concurrently with reads from and writes to the queue.
Honestly I think you would be better off just having Thread 1 spawn a thread (or Task
) whenever it needs something done and using some sort of throttling synchronization (like an AutoResetEvent
)
Upvotes: 1
Reputation: 3466
As long as you are locking on the same "sync" object, thread 2 will wait for thread 1 and vice-versa. I think the ConcurrentQueue is a good idea because it's already specified as thread safe.
Upvotes: 2