Mutex (or "Mutual Exclusion") kernel objects are used to ensure that global variables or resources are accessed exclusively by a piece of code. In this respect, they provide the same functionality as critical sections. However, they are more flexible. For example, critical sections can only be used to ensure exclusivity within a single process, whereas mutex objects can be used across processes. The following steps are required when using mutex kernel objects:
Like all kernel objects, a mutex can either be signaled (in which case WaitForSingleObject will not block), or non-signaled (in which case WaitForSingleObject will block until the object becomes signaled). The function ReleaseMutex changes the mutex state from signaled to non-signaled.
A thread owns a mutex from the time the thread's call to WaitForSingleObject returns until the thread calls ReleaseMutex. In other words, the thread owns the mutex while the mutex is signaled. A mutex can be initially created:
A thread that owns a mutex can terminate before calling ReleaseMutex. In this case, the next thread to call WaitForSingleObject will take ownership of the mutex. However, WaitForSingleObject will return WAIT_ ABANDONED rather than WAIT_OBJECT_0.
Listing 6.2 shows how to use a mutex to control access to a global variable. The code performs the same function as Listing 6.1 but uses a mutex instead of a critical section. The mutex is created by calling CreateMutex, and the second parameter ("TRUE") specifies that that mutex is owned by the thread that creates it. Any other thread that calls WaitForSingleObject will block until the thread that created the mutex calls ReleaseMutex. In Listing 6.2, each thread calls WaitForSingleObject before accessing the global variable g_fValueMutex. One of the thread functions fc1 or fc2 will unblock when the function Listing 6_2 calls ReleaseMutex. The other function will unblock when ReleaseMutex is called by the other unblocked thread function. Listing 6.2 Using a mutexfloat g_fValueMutex = 10.0; DWORD WINAPI fc1(LPVOID); DWORD WINAPI fc2(LPVOID); HANDLE hMutex; void Listing6_2() { HANDLE hThread1, hThread2; DWORD dwThreadID; g_fValueMutex = 10.0; // Create mutex that's initially owned by this thread hMutex = CreateMutex(NULL, TRUE, NULL); hThread1 = CreateThread(NULL, 0, fc1, NULL, 0, &dwThreadID); hThread2 = CreateThread(NULL, 0, fc2, NULL, 0, &dwThreadID); // Release Mutex to allow both threads to // execute their code. ReleaseMutex(hMutex); // Wait until thread 1 and thread 2 completes WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); // Close handle for the mutex and threads CloseHandle(hMutex); CloseHandle(hThread1); CloseHandle(hThread2); cout _T("Finished:") g_fValueMutex endl; } DWORD WINAPI fc1(LPVOID) { WaitForSingleObject(hMutex, INFINITE); g_fValueMutex = g_fValueMutex * g_fValueMutex; ReleaseMutex(hMutex); return 0; } DWORD WINAPI fc2(LPVOID) { WaitForSingleObject(hMutex, INFINITE); g_fValueMutex = (float)3.0 + g_fValueMutex; ReleaseMutex(hMutex); return 0; } The function CreateMutex allows the mutex to be named the last parameter is a string pointer to the mutex's name. If CreateMutex is called with the name of an existing mutex, the existing mutex will be opened rather than creating a new mutex. In this case, CreateMutex returns success, but GetLastError will return ERROR_ALREADY_EXISTS. Many processes can use a named mutex, so this allows mutual exclusion between processes. Windows CE does not support the Win32 function OpenMutex; however, all the functionality of OpenMutex is available through CreateMutex. Listing 4.25 in Chapter 4 ("Property Databases and the Registry") shows how to use a named mutex.
|