Threading in C#: 7 Things you should always remember about

by Pawel Bejger on 21st January 2014

Post image for Threading in C#: 7 Things you should always remember about Have you ever spent a few hours on trying to debug a non-deterministic problem occurring in your multi-threading application? If so, then you should definitely read this article. If not it is anyway a good way of revising your current knowledge about threading challenges in C#.  Being aware of some common facts about threading can help you considerably in building well-designed, error-proof multi-threading applications in the future. 

1. Threads share data if they have a common reference to the same object instance

The code below: When executed, results in: Work done printed on the screen. As you can see in the  example above, both threads (the main one and newThread) call DoWork() method on the same instance of SomeClass. As a result, although the _isWorkDone field is non-static, they share it. In consequence “Work done” is printed on the screen just once, whereas a programmer who is not aware of the above would expect it be printed twice.

2. “Finally” blocks in background threads are not executed when the process terminates

The code below: When executed, results in:   Doing some work… Closing the program…   printed on the screen. As you can see in the example above, when the process terminates (because the main thread has finished its execution) the “finally” block in the background thread is not executed. Not being aware of this this might cause big troubles in case there is “disposal” work to be done at the end, such as closing streams, releasing resources or deleting some temporary files.

3. Captured values in lambda expressions are shared as well

You could presume that the below code: Should result in the following output: 0123456789 Well it doesn’t. The result is completely non-deterministic! The trick here is that the i variable refers to the same memory location throughout the lifetime of the “for” loop. As a result each thread calls the method Console.Write on the same variable which is changing while it’s running. The solution to the above is to use a temporary variable :

4. Locking does not restrict access to the synchronizing object itself in any way.

This means that, if one thread calls: lock(x) and another thread calls x.ToString() the latter will not be blocked. In other words: objects used for locking are lockers not being locked.

5. Try/catch/finally blocks in scope when a thread is created, are of no relevance to the thread when it starts executing

You should be aware that the code below: Will not result in an exception being caught in the “catch” block of Main method. It will unfortunately remain uncaught and will cause the program to shut down. The most natural way of solving the above problem is of course to move the try/catch block to the Divide method.

6. If an object is thread-safe it does not imply that you don’t need to lock around accessing it

Take a look at the code below: Let’s say that the class of _list is fully thread-safe. Nevertheless, there is still a possibility that between checking and adding to the list another thread has already added a certain item. The above example shows that having a thread-safe class does not mean that when you use it the code is still thread-safe.

7. Your program’s instructions can be reordered by compiler, CLR or CPU to improve efficiency.

This one might really be tricky for developers who are not aware of this. To explain this process, let’s firstly take a look at the following piece of code: The question is: is this possible, assuming that A and B are ran concurrently on different threads, that B will write “0” on a screen? Logically thinking it is not: we cannot enter Console.WriteLine before _done is set to true. And to set _done to true we firstly need to assign 1 to _value field. Surprisingly though it is possible, because:
  • The compiler, CLR or CPU may reorder your program’s instructions to improve its efficiency
  • The compiler, CLR or CPU may introduce caching optimizations, such assignments to variables won’t be visible to other threads right away.
Okay, so now the question arises: how to solve such a challenge? Well there are at least a few solutions, but the one that is usually most preferred is a creation of memory fences. Shortly speaking, a full memory barrier (fence), when used, prevents any kind of instruction reordering or caching around this barrier. In C# you call Thread.Barrier to generate such a fence. In the above example the following code would solve our problem: If you would like to know more about different challenges and problems that you can encounter while building multi-threading applications I strongly recommend  a free e-book by Joseph Albahari: Threading in C#. If you know some other interesting scenarios connected with threading in C# feel free to share them with us by leaving a comment! 
  • Stas

    In example 1, there is racing. So some programmers will see “Work Done” printed twice. Or at least there is a chance this to happen. The DoWork method is not thread safe, so the “new Thread” or the Main can evaluate condition “almost” in the same time.
    So the method should look like:
    void DoWork()
    lock(syncLock) {
    if (!_isWorkDone)
    _isWorkDone = true;
    Console.WriteLine(“Work done”);

    where syncLock is private object member of SomeClass.

  • Pingback: Windows App Developer Links – 2014-01-30 | Dan Rigby

  • Mladen Mihajlovic

    in example 3 – the temp variable does not help at all…

  • Paweł Bejger

    First of all: thanks for reading my post and leaving your feedback. Regarding your doubt about the temp variable: The variable temp is local to each loop iteration. Therefore, each thread captures a different memory location. Does this answer your question or you meant something different with stating that it does not help at all?

  • Mladen Mihajlovic

    Hey :) I mean I tried to run it and it didn’t work. The numbers were still in weird orders each time.

  • Marcin Dembowski

    There is no guarantee, that the numbers will be displayed in order. When you are creating a thread and then calling the Start method, then the system (Windows) queues your thread to execute. But immediately in the next iteration you do the same (create a thread and call Start). Now the system queues your thread and executes immediately the last created thread because it’s for example ‘on top of the memory’.
    The temp variable just guarantees, that each thread will point to a unique value which remembers the value of ‘i’, otherwise few threads could point to the same value of ‘i’. To sum up: it’s the systems decision which thread should be executed first.

  • Mladen Mihajlovic

    Great explanation. Thanks. Now I understand the problem and the solution.

  • Pingback: Threading in C#: 7 Things you should always remember about | Eifelmono's Blog