The problem: when parallel (or real asynchronous) control flows (ie: threads) access the _same_ data, you HAVE to protect such accesses. Full stop. There are read/write differences, but, you may think to the problem in a simple way: shared data should be protected so, you should:
==> Suppose you have a global variable called "the_data" used by several threads. You _must_ protect it, so you use a mutex: "the_data_mutex". Think to the mutex as a switch (or a barrier with a big Guardian Minion). Before accessing the_data (potentially accessed by multiple threads) you do : MutexGet(the_data_mutex). You will be allowed to proceed only when the_data is free (and you will sleep until then). After you've done things with the_data you do: MutexRelease(the_data_mutex). Then you get out of the protected area and other threads may be allowed to enter in it.
NB: "local" C variables are really local (they are on the stack and each thread has its own stack). So you never need to protect them, of course.
Here we are. We have protected a simple thing. Of course, you may put a big abstract mutex "the_mutex_for_everything_shared" in your program, and then Get/Release it each time you do something in the global space. But this would be relatively inefficient. So if you have "first_data" and "second_data", and their repective: "first_mutex" and "second_mutex", you think that you should do Get/Release(first_mutex) before accessing first_data, and Get/Release(second_mutex) before accessing second_data. You are right !
But: there is a new problem arising now. Consider 2 threads doing this:
Th. 1 Th. 2 | | MutexGet(first_mutex) MutexGet(second_mutex) | | MutexGet(second_mutex) MutexGet(first_mutex) | | MutexRelease(second_mutex) MutexRelease(first_mutex) | | MutexRelease(first_mutex) MutexRelease(second_mutex) | | + +
Each of these threads seems to do the right thing, but in fact, they don't! We are completely asynchronous, so the worst can arrive. Suppose Th.1 successfully Get the first_mutex, and then just after Th. 2 successfully Get the second_mutex. After that, Th.1 will try to get the second_mutex and Th.2 the first_one, and none of them will never get it. They are waiting for each other to release it so... This is a DEADLOCK.
===> Therefore, the conclusion is: threads should do The Right Thing, i.e. always access the mutexes in the same order !
This is a general rule: always access the mutexes in the same order! If you ever get fb_mutex before gc_mutex, you should always do this in all your threads! Of course, you may access only fb_mutex or gc_mutex independently... But if you get gc_mutex, don't ever try to Get fb_mutex. Okay ?