Shredderroy
Shredderroy

Reputation: 2920

An ambiguity in the documentation on threading in C#

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

Answers (8)

Brian Gideon
Brian Gideon

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

corlettk
corlettk

Reputation: 13574

Roy,

I guess you're "over-reading" a too-concise sentence... I interpret that sentence as meaning two things:

  1. "Just using threadsafe data-structures doesn't mean your program handles multithreading properly... any more than than the presence of threadsafe data-structures inherently makes your program multithreaded"; and he then goes on to say
  2. "Unless you're prepared to put in "the hard yards" involved (it often requires a very precise understanding of quite complex scenarios) to make your WHOLE program handle threading properly, using a threadsafe data-structure is basically a waste of clock-ticks.

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

David Heffernan
David Heffernan

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

anthony sottile
anthony sottile

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

Steve Morgan
Steve Morgan

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

Marc Gravell
Marc Gravell

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

Stephen Cleary
Stephen Cleary

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

vhallac
vhallac

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

Related Questions