Semaphores
Semaphores are synchronization objects that are used to control access to a shared resource by multiple threads. A semaphore maintains a count. Each thread that wants to access the resource must decrement the count (wait) before accessing it. If the count is zero, the thread will block until another thread increments the count (releases) the semaphore.
- Count: The current value of the semaphore.
- Initial Count: The value of the semaphore when it is created.
- Maximum Count: The maximum allowable value for the semaphore's count.
- Waiting: A thread calling
WaitForSingleObject
on a semaphore. If the count is zero, the thread blocks. - Releasing: A thread calling
ReleaseSemaphore
to increment the semaphore's count.
Core Semaphore Functions
The Windows API provides several functions for creating, manipulating, and closing semaphores.
Creating a Semaphore
The CreateSemaphore
function creates a new semaphore object or opens an existing one. It returns a handle to the semaphore.
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName );
Parameters:
lpSemaphoreAttributes
: A pointer to aSECURITY_ATTRIBUTES
structure that specifies the security descriptor for the semaphore. IfNULL
, the semaphore gets a default security descriptor.lInitialCount
: The initial count for the semaphore. This value must be between 0 andlMaximumCount
.lMaximumCount
: The maximum count for the semaphore. Must be greater than 0.lpName
: The name of the semaphore object. IfNULL
, the semaphore is created without a name.
Opening an Existing Semaphore
The OpenSemaphore
function opens an existing semaphore object.
HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName );
Parameters:
dwDesiredAccess
: The access to the semaphore object. This can beSEMAPHORE_ALL_ACCESS
or specific access rights likeSEMAPHORE_MODIFY_STATE
.bInheritHandle
: If this parameter isTRUE
, the handle can be inherited by child processes. Otherwise, it cannot.lpName
: The name of the semaphore object to open.
Releasing a Semaphore
The ReleaseSemaphore
function increments the count of a semaphore object. This can potentially release one or more threads that are waiting for the semaphore.
BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount );
Parameters:
hSemaphore
: A handle to the semaphore object.lReleaseCount
: The amount by which to increment the semaphore's count.lpPreviousCount
: A pointer to a variable that receives the semaphore's previous count. This parameter can beNULL
.
Waiting for a Semaphore
Threads typically wait for a semaphore using the WaitForSingleObject
function, passing the semaphore's handle.
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
Parameters:
hHandle
: A handle to the semaphore object.dwMilliseconds
: The time-out interval, in milliseconds. IfINFINITE
, the function will wait indefinitely.
Return Values:
WAIT_OBJECT_0
: The state of the specified object is signaled (count > 0).WAIT_TIMEOUT
: The time-out interval elapsed.WAIT_FAILED
: An error occurred.
Example Usage
Here's a simplified example illustrating the use of semaphores to limit concurrent access to a resource:
// Assuming MAX_CONCURRENT_USERS is defined const LONG MAX_CONCURRENT_USERS = 5; HANDLE hSemaphore = CreateSemaphore( NULL, // Default security attributes MAX_CONCURRENT_USERS, // Initial count MAX_CONCURRENT_USERS, // Maximum count TEXT("MyResourceSemaphore") // Name ); if (hSemaphore == NULL) { // Handle error return 1; } // In a thread that needs to access the resource: DWORD waitResult = WaitForSingleObject( hSemaphore, // Semaphore handle INFINITE // Wait indefinitely ); if (waitResult == WAIT_OBJECT_0) { // Acquired access to the resource // ... perform operations ... // Release the semaphore when done ReleaseSemaphore( hSemaphore, // Semaphore handle 1, // Release one count NULL // Don't need previous count ); } else { // Could not acquire access (e.g., timeout, error) } // In the main application thread, when the semaphore is no longer needed: CloseHandle(hSemaphore);
Use Cases
- Limiting the number of threads that can access a finite resource (e.g., database connections, limited licenses).
- Implementing producer-consumer scenarios where a semaphore can signal the availability of items in a buffer.
- Controlling the rate at which certain operations can occur.