Synchronization
This section describes the Windows functions and structures used for synchronizing access to resources by multiple threads and processes.
Overview
In a multitasking operating system like Windows, multiple threads and processes can execute concurrently. To prevent data corruption and ensure predictable behavior, it's crucial to synchronize access to shared resources. This involves using synchronization primitives that allow threads to coordinate their actions.
Key Synchronization Objects
- Mutexes (Mutual Exclusion Objects): Provide exclusive access to a resource. Only one thread can own a mutex at a time.
- Semaphores: Control access to a resource that has a limited number of instances. They act as counters.
- Events: Allow threads to signal each other. One thread can signal an event, and other threads can wait for that signal.
- Critical Sections: A lighter-weight mechanism for synchronizing access to data within a single process, typically used for inter-thread synchronization.
- Read/Write Locks: Allow multiple threads to read a resource concurrently, but only one thread to write to it exclusively.
Core Functions
Creating and Opening Synchronization Objects
- CreateMutex: Creates or opens a mutex object.
- CreateSemaphore: Creates or opens a semaphore object.
- CreateEvent: Creates or opens an event object.
- CreateEventEx: Creates or opens an event object with extended attributes.
- InitializeCriticalSection: Initializes a critical section object.
Waiting for Synchronization Objects
- WaitForSingleObject: Waits until the specified object is in the signaled state or the time-out interval elapses.
- WaitForMultipleObjects: Waits until one or all of the specified objects are in the signaled state or the time-out interval elapses.
- SleepConditionVariableCS: Suspends the calling thread until the specified condition variable is woken by
WakeConditionVariableorWakeAllConditionVariable.
Releasing Synchronization Objects
- ReleaseMutex: Releases ownership of a mutex object.
- ReleaseSemaphore: Increments the counter of a semaphore object.
- SetEvent: Sets the state of the specified event object to signaled.
- PulseEvent: Sets the state of the specified event object to signaled, releasing any waiting threads.
- LeaveCriticalSection: Releases ownership of the specified critical-section object.
Interlocked Operations
For simple operations like incrementing or decrementing a counter, atomic interlocked operations can be more efficient than using heavier synchronization primitives.
Example Usage
Here's a simplified example of using a mutex to protect a shared resource:
#include <windows.h>
#include <iostream>
HANDLE hMutex;
int sharedResource = 0;
DWORD WINAPI ThreadFunction(LPVOID lpParam) {
// Wait for the mutex to be available
WaitForSingleObject(hMutex, INFINITE);
// Access the shared resource
sharedResource++;
std::cout << "Thread incremented resource to: " << sharedResource << std::endl;
// Release the mutex
ReleaseMutex(hMutex);
return 0;
}
int main() {
hMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (hMutex == NULL) {
std::cerr << "CreateMutex error: " << GetLastError() << std::endl;
return 1;
}
HANDLE hThread1 = CreateThread(
NULL, // default security attributes
0, // default stack size
ThreadFunction, // function pointer
NULL, // argument for thread
0, // default creation flags
NULL); // thread identifier
HANDLE hThread2 = CreateThread(
NULL, // default security attributes
0, // default stack size
ThreadFunction, // function pointer
NULL, // argument for thread
0, // default creation flags
NULL); // thread identifier
// Wait for threads to finish
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
// Clean up
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hMutex);
return 0;
}
Note: Proper synchronization is critical for avoiding race conditions and deadlocks. Always carefully consider the locking strategy for your application.
Important: Incorrectly used synchronization primitives can lead to severe performance issues or program instability. Refer to the detailed documentation for each function.