Reputation: 2920
While reading Joe Albahari's excellent book "Threading in C#" I came across the following ambiguous sentence:
A thread-safe type does not necessarily make the program using it thread-safe, and often the work involved in the latter makes the former redundant.
(You can find the sentence on this page; just search for "indeterminacy" to quickly jump to the appropriate section.)
I am looking to use a ConcurrentDictionary to implement certain thread-safe data structures. Is the paragraph telling me that ConcurrentDictionary does not guarantee thread-safe writes to my data structure? Can someone please provide a counter-example that shows a thread-safe type actually failing to provide thread safety?
Thanks in advance for your help.
Upvotes: 0
Views: 176
Reputation: 48949
Is the paragraph telling me that ConcurrentDictionary does not guarantee thread-safe writes to my data structure?
No, that is not what Joe Albahari means. ConcurrentDictionary
will always maintain a consistent state through simultaneous writes from multiple threads. Another thread will never see the data structure in an inconsistent state.
Can someone please provide a counter-example that shows a thread-safe type actually failing to provide thread safety?
However, a series of reads and writes from a thread-safe type may still fail in a multithreaded environment.
void ExecutedByMultipleThreads(ConcurrentQueue<object> queue)
{
object value;
if (!queue.IsEmpty)
{
queue.TryDequeue(out value);
Console.WriteLine(value.GetHashCode());
}
}
So clearly ConcurrentQueue
is a thread-safe type, but this program can still fail with a NullReferenceException
if another thread dequeued the last item between the IsEmpty
and TryDequeue
methods. The data structure itself still provides its thread-safety guarentee by remaining in a consistent state, but the program is not thread-safe by assumptions it make about thread-safety in a general are not correct. In this case its the program that is incorrect; not the data structure.
Upvotes: 0
Reputation: 13574
Roy,
I guess you're "over-reading" a too-concise sentence... I interpret that sentence as meaning two things:
Ergo: Multi-threading is pretty hard, using appropriate out-of-the-box datastructures is an important part of any solution, but it's certainly NOT the whole solution... and unless you're prepared to think-it-through (i.e. do your syncronization home-work) you're just kidding yourself that a data-structure will somehow magically "fix" your program.
I know that sounds "a bit harsh" but my perception is that a lot of noobs are really disappointed when they discover that programming (still, in this enlightened age of animiated icons and GUI painters) requires Deep Thought. Who'd've thunk it?!?!
Cheers. Keith.
Upvotes: 0
Reputation: 613013
My concise explanation is this. There are many forms of thread safety and code that satisfies one form does not automatically satisfy all the others.
Upvotes: 1
Reputation: 69964
Was doing some searching a while back to fix a problem I had with some threading and came across this page:
http://www.albahari.com/threading/part2.aspx#_Thread_Safety
Particularly the section on "Locking around thread-safe objects"
From the page:
Sometimes you also need to lock around accessing thread-safe objects. To illustrate, imagine that the Framework’s List class was, indeed, thread-safe, and we want to add an item to a list:
if (!_list.Contains (newItem)) _list.Add (newItem);
Whether or not the list was thread-safe, this statement is certainly not!
Upvotes: 4
Reputation: 13091
It's a bit of a vague statement, but consider for example, a class has two members, each of which is thread-safe, but that must both be updated in an atomic manner.
In dealing with that situation, you're likely to make that entire operation atomic, and thus thread-safe, rendering the thread-safe access to the individual members irrelevant.
If doesn't mean that your ConcurrentDictionary is going to behave in an unsafe way.
Upvotes: 1
Reputation: 1062965
At the simplest, a thread safe list or dictionary is a good example; having each individual operation thread safe isn't always enough - for example, "check if the list is empty; if it is, add an item" - even if all thread-safe, you can't do:
if(list.Count == 0) list.Add(foo);
as it could change between the two. You need to synchronize the test and the change.
Upvotes: 8
Reputation: 456647
I think what he means is that just using ConcurrentDictionary
instead of Dictionary
everywhere isn't going to make the program thread-safe. So, if you have a non-thread-safe program, a search and replace isn't going to help; likewise, adding SynchronizedAttribute
everywhere isn't going to work like a magic fairy dust. This is particularly true regarding collections, where iteration is always a problem[1].
On the other hand, if you restructure the non-thread-safe program into a more thread-safe design, then you often don't need thread-safe data structures. One popular approach is to redefine the program in terms of "actors" that send "messages" to each other - aside from a single producer/consumer-style message queue, each actor can stand alone and does not need to use thread-safe data structures internally.
[1] The first release of BCL collections included some "thread-safe" collections that just plain were not thread-safe during iterations. The Concurrent collections are thread-safe during iteration, but iterate concurrently with other threads' modifications. Other collection libraries allow "snapshots" which can then be iterated, ignoring modifications from other threads.
Upvotes: 2
Reputation: 13917
My understanding of the warning is that just because you are using thread safe variables does not mean that your program is thread safe.
As an example, consider a class that has two variables that can be modified from two threads. Just because these variables are individually thread safe doesn't guarantee atomicity of modifications to the class. If there are two threads modifying these variables, it is possible that one variable will end up with the value set by one thread, while the other gets set by another thread. This can easily break the internal consistency of the class.
Upvotes: 4