Multithreaded (Parallel) programming is difficult for many reasons. Developer always has to put extra care to protect the program from issues like race condition, deadlocks, livelocks, priority inversions, two-step dances, and lock convoys. My personal experience says these all issues are tough to identify and then resolve.
It is always better to have understanding on these issues before writing multithreaded application. In this article I have covered the quick overview of following issues associated with multithreaded programming:
A race condition occurs when two or more threads are able to access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any point, we cannot know the order at which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are ‘racing’ to access/change the data.
Often problems occur when one thread does a “check-then-act” (e.g. “check” if the value is X, and then “act” to do something that depends on the value being X) and another thread does something to the value in between the “check” and the “act”.
In order to prevent race conditions occurring, typically put a lock around the shared data to ensure that only one thread can access the data at a time. (Read more about how to put lock and different techniques to synchronize the threads – C#: Thread Synchronization)
A deadlock occurs when two or more processes/threads are unable to proceed because each is waiting or one of the others to do something.
A common example is a program communicating to a server, which may find itself waiting for output from the server before sending anything more to it, while the server is similarly waiting for more input from the controlling program before outputting anything.
Another common example in which each process is trying to send stuff to the other but all buffers are full because nobody is reading anything.
Another example, common in database programming, is two processes that are sharing some resource (e.g. read access to a table) but then both decide to wait for exclusive (e.g. write) access.
The real world example will be interactions between humans, as when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they always move the same way at the same time.
Other issues of multithreaded programming like “LiveLock“, “Priority Inversion“, “Two-Step Dances” and “Lock Convoys” are covered in “Issues with Multithreaded Programming : Part 2” and “Issues with Multithreaded Programming – Part 3”.