Friday, May 31, 2013

mutex tutorial

Mutexes

A mutex is an OS-level synchronization primitive that can be used to ensure a section of code can only be executed by one thread at a time.
It has two states: locked and unlocked. When the mutex is locked, any further attempt to lock it will block (the calling thread will be suspended). When the mutex becomes unlocked, if there are threads waiting one of these will be resumed and will lock the mutex. Furthermore, the mutex may only be unlocked by the thread that locked it.
If we have a resource we need to share between threads, we associate a mutex with it and use the mutex to synchronize resource access. All we need to do is ensure our code locks the mutex before using the resource, and unlocks it after it is finished. This will prevent race conditions related to multiple threads simultaneously accessing that resource.

Diagram 1. Two thread contention for a mutex


Note: We will be updating mutex section above soon with more documents and sample codes.

Mutexes in Practice - Boost.Threads solution

Boost.Threads is a part of the excellent Boost libraries. It has been intelligently designed to enhance safety by making error-prone code more difficult to write.
We'll be using Boost.Threads throughout the tutorial, since we may as well get used to using a well designed C++ library from the outset. Furthermore, the upcoming C++ 0x standard (due sometime this decade) will use Boost.Threads as the model for the new threading support, so learning this library will help future-proof your C++ skills.
Listing 2. Boost.Threads synchronisation
int sharedCounter = 50;
boost::mutex counterMutex;
 
void solutionWorkerThread()
{
    while(sharedCounter > 0)
    {
        bool doWork = false;
        {
            // scoped_lock locks mutex
            boost::mutex::scoped_lock lock(counterMutex);
            if(sharedCounter > 0)
            {
                --sharedCounter;
                doWork = true;
            }
            // scoped_lock unlocks mutex automatically at end of scope
        }
 
        if(doWork) doSomeWork();
    }
}
In the above solution, the shared counter is checked and updated as an atomic operation (with respect to multiple threads) so the race condition is solved.
Note the way the scoped_lock works: the constructor locks the associated mutex and the destructor unlocks it. This is the RAII (Resource Acquisition Is Initialization idiom, and it helps exception safety. If an exception were thrown while we had locked the mutex, the scoped_lock would be destroyed during the normal stack unwinding process and the mutex would be automatically freed.
Exception safety is not an issue with this simple example, since no statement can throw while we have the mutex locked. However, real-world code will almost always benefit from the scoped_lock design.
Unfortunately concurrent code can have many problems: race conditions are only the most fundamental. The next problem we'll cover is called Deadlock, and it commonly arises from the interaction of blocking mutex.
See also:

No comments:

Post a Comment