If a Queue is syncronized:
var _item = Queue.Synchronized(new Queue());
can I call methods like Enqueue and Dequeue on it without using lock statements?
My current code is:
lock (_item.SyncRoot)
{
_item.Enqueue(obj);
}
Can I thread-safely use:
_item.Enqueue(obj);
var item = _item.Dequeue();
The call to Enqueue and the call to Dequeue are thread safe.
However, your sample code is not:
Between the call to Enqueue and the call to Dequeue there could have been a thread switch. This means, that item might be another instance than obj or the call to Dequeue throws an exception, because it now is empty.
To make your sample code thread safe, you still need to lock explicitly:
lock(_item.SyncRoot)
{
_item.Enqueue(obj);
var item = _item.Dequeue);
}
Only now it is guaranteed, that item reference-equals obj in all circumstances.
That is pretty much what SynchronizedQueue does, but there is a problem... typically you need to check the .Count and .Dequeue() in one atomic unit - not check the .Count (one unit) then .Dequeue() (another unit) - you can't trust .Count at all once the lock is surrendered, and .Dequeue() will throw if another thread has stolen the work.
Maybe try ConcurrentQueue<T> in 4.0 (with .TryDequeue()), or use Queue<T> and lock.
From MSDN
To guarantee the thread safety of the
Queue, all operations must be done
through this wrapper only.
Enumerating through a collection is
intrinsically not a thread-safe
procedure. Even when a collection is
synchronized, other threads can still
modify the collection, which causes
the enumerator to throw an exception.
To guarantee thread safety during
enumeration, you can either lock the
collection during the entire
enumeration or catch the exceptions
resulting from changes made by other
threads.
Just as John Skeet's answer suggests here, you might be better or using locking since enumerating might cause an exception.
Gregs answer also talks about what Marc mentions with the Count not being thread safe.
Related
Both collections, Queue and ConcurrentQueue have a method TryDequeue. What is the difference between using TryDequeue with Queue and ConcurrentQueue respectively? Is Queue's TryDequeue method thread-safe in multi-threading environment?
Queue.TryDequeue() is not threadsafe.
We can look at its implementation for proof:
public bool TryDequeue([MaybeNullWhen(false)] out T result)
{
int head = _head;
T[] array = _array;
if (_size == 0)
{
result = default;
return false;
}
result = array[head];
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
array[head] = default!;
}
MoveNext(ref _head);
_size--;
_version++;
return true;
}
It's easy to see that isn't threadsafe. Just the _size-- alone is not threadsafe.
But even without the source code, the documentation for Queue<T> explicitly states:
Any instance members are not guaranteed to be threadsafe.
Of course, the methods of ConcurrentQueue are threadsafe, and by definition ImmutableQueue is also threadsafe.
(The Try in the name of TryDequeue() is referring to the fact that it handles an empty queue rather than anything to do with thread safety.)
Nothing on Queue<T> is thread-safe - not even Count
The difference is entirely: thread-safety. On ConcurrentQueue<T>, yes: it is thread-safe. There are also some minor API differences (the lack of Dequeue() on the concurrent version, for example), but mostly: the API shape is directly comparable, give or take thread-safety.
TL; DR: "Any instance members are not guaranteed to be thread safe. A Queue can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. For a thread-safe queue, see ConcurrentQueue<T>.", learn.microsoft.com
Background
OP:
Both collections, Queue and ConcurrentQueue have a method TryDequeue. What is the difference between using TryDequeue with Queue and ConcurrentQueue respectively?
If we take a look at the "Remarks" section of Queue we see:
MSDN (my emphasis):
Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.1
Interestingly, though with dragons, there is this additional point (again my emphasis):
A Queue can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. For a thread-safe queue, see ConcurrentQueue.1
OP:
Is Queue's TryDequeue method thread-safe in multi-threading environment?
So it would appear Queue<T> has some concurrent features though great care must be undertaken on the part of the user whilst doing so (ensuring the queue is not modified).
Now let's compare that with something written from the ground up with concurrency in mind - ConcurrentQueue<T>.
First a word from MSDN:
Represents a thread-safe first in-first out (FIFO) collection.
All public and protected members of ConcurrentQueue are thread-safe and may be used concurrently from multiple threads.2
So this is confirmation that any instance method of ConcurrentQueue<T> is thread-safe, unlike Queue<T>.
TryDequeue compared
The documentation for the definition of both methods begins essentially the same:
Removes the object at the beginning of the Queue, and copies it to the result parameter.3
Tries to remove and return the object at the beginning of the concurrent queue.4
...however we already know that Queue<T> instance methods are not thread safe for concurrent read/write. ConcurrentQueue<T> being the champ that is ends the match with a detailed explanation as to the internal workings:
ConcurrentQueue handles all synchronization internally. If two threads call TryDequeue at precisely the same moment, neither operation is blocked. When a conflict is detected between two threads, one thread has to try again to retrieve the next element, and the synchronization is handled internally.4
...and:
TryDequeue tries to remove an element from the queue. If the method is successful, the item is removed and the method returns true; otherwise, it returns false. That happens atomically with respect to other operations on the queue.4
Tell me more
1 "Thread safety - Queue class", Microsoft
2 ConcurrentQueue Class, Microsoft
3 Queue<T>.TryDequeue(T) Method, Microsoft
4 ConcurrentQueue.TryDequeue(T) Method, Microsoft
Consider the following code:
static void AddItem()
{
lock (_list)
_list.Add ("Item " + _list.Count); //Lock 1
string[] items;
lock (_list)
items = _list.ToArray(); //Lock 2
foreach (string s in items)
Console.WriteLine (s);
}
If Thread A gets Lock 2, and Thread B attempts to get Lock 1, will B get the lock or not? Considering both locks use the same locking object.
No, thread B will need to wait until thread A releases the lock. That's the point of it being the same lock object, after all - there's one lock. Where the lock is acquired or released is irrelevant: only one thread can "own" the monitor at a time.
I would strongly advise you to use braces for readability, by the way:
lock(_list)
{
_list.Add(...);
}
No, B will not. Both are locking on the same object and therefore the two locks are "linked." For this reason, if you need to highly-optimise such code, there are times where you might consider multiple lock objects.
As a side note, you should not be locking on the list itself but on an object created specifically for that purpose.
No, since they use the same locking object, they are mutually exclusive.
Often code is used to lock an object (for example a list) to perform an operation on it without interference from other threads. This requires that the item is locked no matter what operation is performed.
To elaborate, say you have a list that is designed to be threadsafe. If you try adding and deleting multiple items simultaneously, you could corrupt the list. By locking the list whenever it needs to be modified, you can help ensure thread safety.
This all depends on the fact that one object will make all locks mutually exclusive.
If Thread A is using the lock, then no other thread can use it (regardless of where the lock is being used). So, thread B will be blocked until that lock is free.
Consider that:
lock(obj)
{
//Do Stuff;
}
Is shorthand for:
Monitor.Enter(obj);
try
{
//Do Stuff;
}
finally
{
Monitor.Exit(obj);
}
Now consider that Monitor.Enter() is a method call like any other. It knows nothing about where in the code it was called. The only thing it knows about, is what object was passed to it.
As far as it's concerned, what you call "Lock 1" and "Lock 2" are the same lock.
Is there a difference in the below code segments in the way we lock?
public Hashtable mySet= new Hashtable() //mySet is visible to other threads.
lock (mySet)
{
mySet.Add("Hello World");
}
and
public Hashtable mySet= new Hashtable();
lock(mySet.SyncRoot)
{
mySet.Add("Hello World");
}
lock doesn't actually lock the object in question, so it makes no difference which object is used. Instead it uses the object to establish a protocol and as long as all threads use the same object the protocol guarantees that only one thread will execute code guarded by that lock.
You can think of the object as the microphone on a talk show. Whoever holds the microphone is the only one allowed to talk (I know that is not always how it turns out on some of the shows, but that's the idea anyway).
As the object passend to the lock will only be used as a "flag holder", this will not make any difference.
Please see this
According to the MSDN documentation here only a lock on the SyncRoot of a collection does guarantee thread safety.
Enumerating through a collection is intrinsically not a thread-safe
procedure. Even when a collection is synchronized, other threads can
still modify the collection, which causes the enumerator to throw an
exception. To guarantee thread safety during enumeration, you can
either lock the collection during the entire enumeration or catch the
exceptions resulting from changes made by other threads.
When doing thread synchronization in C# should I also lock an object when I read a value or just changing it?
for example I have Queue<T> object. Should I just lock it when doing the Enqueue and Dequeue or should I also lock it when checking values like Count?
From MSDN:
A Queue<(Of <(T>)>) can support
multiple readers concurrently, as long
as the collection is not modified.
Even so, enumerating through a
collection is intrinsically not a
thread-safe procedure. To guarantee
thread safety during enumeration, you
can lock the collection during the
entire enumeration. To allow the
collection to be accessed by multiple
threads for reading and writing, you
must implement your own
synchronization.
You should ensure no reader is active while an item is queued (a lock is probably a good idea).
Looking at the count in reflector reveals a read from a private field. This can be okay depending on what you do with the value. This means you shouldn't do stuff like this (without proper locking):
if(queue.Count > 0)
queue.Dequeue();
Depends on what you want to do with lock. Usually this kind of locking needs a reader/writer locking mechanism.
Readers/writers locking means that readers share a lock, so you can have multiple readers reading the collection simultaneously, but to write, you should acquire an exclusive lock.
If you don't lock it, you may get an older value. A race condition could occur such that a write operation is performed changing Count, but you would get the value before the change. For example, if the queue has only one item, and a thread calls dequeue, another thread may read the count, find it still 1, and call dequeue again. The second call won't be done until the lock is granted, but at that time the queue would actually be empty.
The CLR guarantees atomic reads for values up to the width of the processor. So if you're running on 32 bit, reading ints will be atomic. If you're running on 64 bit machine, reading longs will be atomic. Ergo, if Count is an Int32 there's no need to lock.
This post is pertinent to your question.
I have a Queue object that I need to ensure is thread-safe. Would it be better to use a lock object like this:
lock(myLockObject)
{
//do stuff with the queue
}
Or is it recommended to use Queue.Synchronized like this:
Queue.Synchronized(myQueue).whatever_i_want_to_do();
From reading the MSDN docs it says I should use Queue.Synchronized to make it thread-safe, but then it gives an example using a lock object. From the MSDN article:
To guarantee the thread safety of the
Queue, all operations must be done
through this wrapper only.
Enumerating through a collection is
intrinsically not a thread-safe
procedure. Even when a collection is
synchronized, other threads can still
modify the collection, which causes
the enumerator to throw an exception.
To guarantee thread safety during
enumeration, you can either lock the
collection during the entire
enumeration or catch the exceptions
resulting from changes made by other
threads.
If calling Synchronized() doesn't ensure thread-safety what's the point of it? Am I missing something here?
Personally I always prefer locking. It means that you get to decide the granularity. If you just rely on the Synchronized wrapper, each individual operation is synchronized but if you ever need to do more than one thing (e.g. iterating over the whole collection) you need to lock anyway. In the interests of simplicity, I prefer to just have one thing to remember - lock appropriately!
EDIT: As noted in comments, if you can use higher level abstractions, that's great. And if you do use locking, be careful with it - document what you expect to be locked where, and acquire/release locks for as short a period as possible (more for correctness than performance). Avoid calling into unknown code while holding a lock, avoid nested locks etc.
In .NET 4 there's a lot more support for higher-level abstractions (including lock-free code). Either way, I still wouldn't recommend using the synchronized wrappers.
There's a major problem with the Synchronized methods in the old collection library, in that they synchronize at too low a level of granularity (per method rather than per unit-of-work).
There's a classic race condition with a synchronized queue, shown below where you check the Count to see if it is safe to dequeue, but then the Dequeue method throws an exception indicating the queue is empty. This occurs because each individual operation is thread-safe, but the value of Count can change between when you query it and when you use the value.
object item;
if (queue.Count > 0)
{
// at this point another thread dequeues the last item, and then
// the next line will throw an InvalidOperationException...
item = queue.Dequeue();
}
You can safely write this using a manual lock around the entire unit-of-work (i.e. checking the count and dequeueing the item) as follows:
object item;
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
}
}
So as you can't safely dequeue anything from a synchronized queue, I wouldn't bother with it and would just use manual locking.
.NET 4.0 should have a whole bunch of properly implemented thread-safe collections, but that's still nearly a year away unfortunately.
There's frequently a tension between demands for 'thread safe collections' and the requirement to perform multiple operations on the collection in an atomic fashion.
So Synchronized() gives you a collection which won't smash itself up if multiple threads add items to it simultaneously, but it doesn't magically give you a collection that knows that during an enumeration, nobody else must touch it.
As well as enumeration, common operations like "is this item already in the queue? No, then I'll add it" also require synchronisation which is wider than just the queue.
This way we don't need to lock the queue just to find out it was empty.
object item;
if (queue.Count > 0)
{
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
}
}
}
It seems clear to me that using a lock(...) {...} lock is the right answer.
To guarantee the thread safety of the Queue, all operations must be done through this wrapper only.
If other threads access the queue without using .Synchronized(), then you'll be up a creek - unless all your queue access is locked up.