Would it be thread-safe if I solely call Enqueue(T)
from multiple threads simultaneously and wait for those threads to complete before calling Dequeue()
or enumerating the queue?
var queue = new Queue<int>();
Action enqueue = () =>
{
for (int i = 0; i < 100000; i++)
queue.Enqueue(i);
};
var tasks = new[]
{
new Task(enqueue),
new Task(enqueue),
new Task(enqueue)
};
foreach (var task in tasks)
task.Start();
Task.Factory.ContinueWhenAll(tasks, t =>
{
while (queue.Count > 0)
Console.WriteLine(queue.Dequeue());
});
The Queue module provides a FIFO implementation suitable for multi-threaded programming. It can be used to pass messages or other data between producer and consumer threads safely.
First of all, the answer is NO. The method is not thread-safe, because the counter++ operation is not atomic, which means it consists more than one atomic operations. In this case, one is accessing value and the other is increasing the value by one.
C# Queues can support multiple readers simultaneously. However, even when a collection is synchronized, it can still be modified by other threads, which will cause the enumerator to throw an exception. Generally, enumerating through a collection is not considered a thread-safe procedure.
The documentation also states that instance members of this type are not thread safe (scroll down to the Thread Safety section).
The documentation also states:
A Queue can support multiple readers concurrently, as long as the collection is not modified.
However, this is just a by-product of the fact that concurrent reading does not mutate the list. It does not make the type "thread-safe". Thread-safety is best thought of as offering true support across all actions that define the public contract of the type (in this case, thread-safety in mutating the list also).
More tongue-in-cheek: the implementation of Enqueue doesn't include any thread synchronisation or locking primitives:
public void Enqueue(T item)
{
if (this._size == this._array.Length)
{
int num = (int)((long)this._array.Length * 200L / 100L);
if (num < this._array.Length + 4)
{
num = this._array.Length + 4;
}
this.SetCapacity(num);
}
this._array[this._tail] = item;
this._tail = (this._tail + 1) % this._array.Length;
this._size++;
this._version++;
}
So I'm going with "no". There is ConcurrentQueue
for multi-threaded support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With