views:

644

answers:

3

Could you describe two methods of synchronizing multi-threaded write access performed on a class member?

Please could any one help me what is this meant to do and what is the right answer.

+11  A: 

In multithreaded applications, there are many situations where simultaneous access to the same data can cause problems. In such cases synchronization is required to guarantee that only one thread has access at any one time.

I imagine they mean using the lock-statement (or SyncLock in VB.NET) vs. using a Monitor.

You might want to read this page for examples and an understanding of the concept. However, if you have no experience with multithreaded application design, it will likely become quickly apparent, should your new employer put you to the test. It's a fairly complicated subject, with many possible pitfalls such as deadlock.

There is a decent MSDN page on the subject as well.

There may be other options, depending on the type of member variable and how it is to be changed. Incrementing an integer for example can be done with the Interlocked.Increment method.

As an excercise and demonstration of the problem, try writing an application that starts 5 simultaneous threads, incrementing a shared counter a million times per thread. The intended end result of the counter would be 5 million, but that is (probably) not what you will end up with :)

Edit: made a quick implementation myself (download). Sample output:

Unsynchronized counter demo:
expected counter = 5000000
actual counter   = 4901600
Time taken (ms)  = 67

Synchronized counter demo:
expected counter = 5000000
actual counter   = 5000000
Time taken (ms)  = 287
Thorarin
Thanks for the answer. I would like to know more about multi threading in .NET (c#). Infact I need to know more about c# in general though. Would you suggest me one book that can transform an ordinary developer to next generation developer.
Supremestar
No specific titles come to mind, but I don't use books much. In fact it's pretty amazing that I pre-ordered C# in depth, second edition. Good stuff, but there are probably books more suited to your needs.
Thorarin
"C# in Depth" is an awesome book.
TTT
[fawn]Written by an awesome person[/fawn] :) I mostly ordered it to slap colleagues around with when they do something stupid :)
Thorarin
when you said "there are probably books more suited to your needs", you mean c# in depth is too complicated for me?
Supremestar
@Supremestar: If you're new to C#, then C# in Depth wouldn't be much use to you (yet). You could try C# in a Nutshell, or perhaps the most recent printing of Head First C#. (Do make sure you get a recent printing, as lots of errors have been corrected.)
Jon Skeet
@Jon Skeet: thanks for the suggestions. C# in a Nutshell seemed pretty good indeed (although I've only browsed it a little).
Thorarin
+7  A: 

When you change data in C#, something that looks like a single operation may be compiled into several instructions. Take the following class:

public class Number {
    private int a = 0;
    public void Add(int b) {
        a += b;
    }
}

When you build it, you get the following IL code:

IL_0000:  nop
IL_0001:  ldarg.0
IL_0002:  dup
// Pushes the value of the private variable 'a' onto the stack
IL_0003:  ldfld      int32 Simple.Number::a
// Pushes the value of the argument 'b' onto the stack
IL_0008:  ldarg.1
// Adds the top two values of the stack together
IL_0009:  add
// Sets 'a' to the value on top of the stack
IL_000a:  stfld      int32 Simple.Number::a
IL_000f:  ret

Now, say you have a Number object and two threads call its Add method like this:

number.Add(2); // Thread 1
number.Add(3); // Thread 2

If you want the result to be 5 (0 + 2 + 3), there's a problem. You don't know when these threads will execute their instructions. Both threads could execute IL_0003 (pushing zero onto the stack) before either executes IL_000a (actually changing the member variable) and you get this:

a = 0 + 2; // Thread 1
a = 0 + 3; // Thread 2

The last thread to finish 'wins' and at the end of the process, a is 2 or 3 instead of 5.

So you have to make sure that one complete set of instructions finishes before the other set. To do that, you can:

1) Lock access to the class member while it's being written, using one of the many .NET synchronization primitives (like lock, Mutex, ReaderWriterLockSlim, etc.) so that only one thread can work on it at a time.

2) Push write operations into a queue and process that queue with a single thread. As Thorarin points out, you still have to synchronize access to the queue if it isn't thread-safe, but it's worth it for complex write operations.

There are other techniques. Some (like Interlocked) are limited to particular data types, and there are even more (like the ones discussed in Non-blocking synchronization and Part 4 of Joseph Albahari's Threading in C#), though they are more complex: approach them with caution.

Jeff Sternal
+1 for ReadWriterLockSlim. However, I think the OP needs to familiarize himself with multi-threaded programming more before grasping that concept fully. As for your second point: you would need to synchronize the queue, just complicating your problem.
Thorarin
True enough, and your answer provides a far superior introduction to those concepts (though I'm trying to take a different tack in an update). About the queue, it's true that has to be synchronized too, though there are cases where that approach simplifies things rather than complicating them!
Jeff Sternal
A: 

There are a couple of ways, several of which are mentioned previously.

  1. ReaderWriterLockSlim is my preferred method. This gives you a database type of locking, and allows for upgrading (although the syntax for that is incorrect in the MSDN last time I looked and is very non-obvious)
  2. lock statements. You treat a read like a write and just prevent access to the variable
  3. Interlocked operations. This performs an operations on a value type in an atomic step. This can be used for lock free threading (really wouldn't recommend this)
  4. Mutexes and Semaphores (haven't used these)
  5. Monitor statements (this is essentially how the lock keyword works)

While I don't mean to denigrate other answers, I would not trust anything that does not use one of these techniques. My apologies if I have forgotten any.

Steve