Threads Module User's Guide : PART II Concurrency Packages : Chapter 4 The Synchronization Package : Getting Started : Using Mutexes
Using Mutexes
A mutex implements a form of synchronization called mutual exclusion, where the presence of one thread within a protected or critical section of code prohibits the entry of other threads into that section. A mutex can be used to synchronize thread access to shared resources and data.
A thread must acquire ownership of a mutex prior to entering the section of code protected by the mutex. Once a thread is granted ownership of the mutex, it is allowed to proceed into the protected section of code. Other threads entering this same section also attempt to acquire ownership of the mutex, but these threads are forced to wait until the current owner exits the section of code and releases ownership of the mutex.
The RWMutexLock class is the basic mutex mechanism for the Synchronization package. See “The RWMutexLock Class” for more information about this class.
Using a Basic Mutex Mechanism
By using the RWMutexLock class, the hello() function can be modified, as in Example 26, so that each “Hello World!” message is produced without interference.
Example 26 – Avoiding interference between threads
void hello(void)
{
// Declare a static mutex instance that will
// be shared by all threads calling this function
static RWMutexLock mutex;
 
for(int i=0;i<100;i++) {
 
// Acquire mutex to block other threads
mutex.acquire();
 
cout << "Hello” << "World!” << endl;
 
// Release the mutex so other threads can say "hello”
mutex.release();
}
}
By adding synchronization, the code now produces the desired output. Each message appears without being interleaved with the messages produced by the other thread:
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
.
.
Using a Try-Catch Block
A potential problem with this second implementation is that if one of the iostream operations produces an exception, the mutex.release() statement is not executed, leaving the mutex locked forever.
One way to fix this problem is to add a try-catch block around the output operation and code a second mutex release in the catch block:
Example 27 – Using a try-catch block to ensure the mutex is released
void hello(void)
{
// Declare a static mutex instance that will
// be shared by all threads calling this function
static RWMutexLock mutex;
 
for(int i=0;i<100;i++) {
 
// Acquire mutex to block other threads
mutex.acquire();
 
try {
cout << "Hello” << "World!” << endl;
}
catch(...) {
// Exception occurred!
// Release the mutex and rethrow
mutex.release();
throw;
}
// Release the mutex so other threads can say "hello”
mutex.release();
}
}
With this code, the mutex is released, even if an exception is thrown by an iostream operation. (The mutex state might not be important once an exception has occurred, but assume that it is for the purpose of this discussion.)
This solution is sufficient for a simple example, but if unique handlers for several different exceptions are required, you must remember to code the mutex release in each catch block that you define. This can be cumbersome and can introduce difficult-to-detect coding mistakes.