Waking a thread from sleep when the collection changes
Thank you all for the suggested options. I finally settled on AutoResetEvent for this requirement. After using the first object in the queue, instead of putting the main thread in "Sleep", I spawned a new thread from the main thread, where I called sleep. The main thread will just wait. As soon as the new thread wakes up, it will signal the main thread using Set and the main thread will resume. This is one part.
Second part. If any other thread changes the queue, even that thread calls Set on the same EventWaitHandle, thereby resuming the main thread again.
This may not be the optimal solution, but simpler than other approaches.
source to share
You should use a collection specifically designed for this purpose. One example is BlockingCollection
which allows you to take an item from a collection, and if there are no items to be accepted, the method will block until the item is provided to you. It is also a collection specially designed to handle multiple threads, lightening your sync load.
Note that it BlockingCollection
can be initialized to support different collection types. It will use it by default ConcurrentQueue
, but there are other collections in the namespace System.Collections.Concurrent
that you can use if you don't need the semantics of the queue (you seem to do that). You can also implement your own collection implementing IProducerConsumerCollection<T>
if you really need something unique.
source to share
I would recommend against using it anyway Thread.Sleep()
, because it blocks the thread entirely.
It is much better to use AutoResetEvent
or ManualResetEvent
to synchronize two or more threads:
https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx
source to share
Servy has the correct answer for this using a Blocking Collection .
Just add another one: it creates a new thread with an empty thread when the "work" items become available in the queue and processes them asynchronously on that thread.
You can use it in a producer / consumer queue:
eg:.
/// <summary>
/// Producer/consumer queue. Used when a task needs executing, itβs enqueued to ensure order,
/// allowing the caller to get on with other things. The number of consumers can be defined,
/// each running on a thread pool task thread.
/// Adapted from: http://www.albahari.com/threading/part5.aspx#_BlockingCollectionT
/// </summary>
public class ProducerConsumerQueue : IDisposable
{
private BlockingCollection<Action> _taskQ = new BlockingCollection<Action>();
public ProducerConsumerQueue(int workerCount)
{
// Create and start a separate Task for each consumer:
for (int i = 0; i < workerCount; i++)
{
Task.Factory.StartNew(Consume);
}
}
public void Dispose()
{
_taskQ.CompleteAdding();
}
public void EnqueueTask(Action action)
{
_taskQ.Add(action);
}
private void Consume()
{
// This sequence that weβre enumerating will block when no elements
// are available and will end when CompleteAdding is called.
// Note: This removes AND returns items from the collection.
foreach (Action action in _taskQ.GetConsumingEnumerable())
{
// Perform task.
action();
}
}
}
source to share
I would put the thread into iteration while
and then reduce the sleeptimer to about 200 milliseconds.
But on each iteration, I checked if the queue had changed. Thus, the thread is constantly in sleep mode and wakes up when the queue has been changed.
If you want to stop the thread, you just set the condition while
to false
.
source to share