Semaphores are commonly used in operating systems and concurrent programs where the goal is to manage concurrent access to a pool of resources. When using a semaphore, the goal isn’t who owns what, but how many resources are still available. Semaphores are different from mutexes in several ways:
Semaphores need not be in a binary (locked or unlocked) state. A special type of semaphore called a counting semaphore can range in value from 0 to some r, where r is the number of possible resources. Any time a resource is produced, the semaphore is incremented. Any time a resource is being used, the semaphore is decremented. When a counting semaphore has a value of 0, it means that no resources are available, and any other threads that attempt to acquire a resource must wait (e.g., block).
Semaphores can be locked by default.
While a mutex and condition variables can simulate the functionality of a semaphore, using a semaphore may be simpler and more efficient in some cases. Semaphores also have the advantage that any thread can unlock the semaphore (in contrast to a mutex, where the calling thread must unlock it).
Semaphores are not part of the Pthreads library, but that does not mean that
you cannot use them. On Linux and macOS systems, semaphore primitives can
be accessed from
semaphore.h, typically located in
Since there is no standard, the function calls may differ on different
systems. That said, the semaphore library has similar declarations to
those of mutexes:
Declare a semaphore (type
Initialize a semaphore using
sem_initfunction has three parameters: the first is the address of a semaphore, the second is its initial state (locked or unlocked), and the third parameter indicates whether the semaphore should be shared with the threads of a process (e.g., with value 0) or between processes (e.g., with value 1). This is useful because semaphores are commonly used for process synchronization. For example, initializing a semaphore with the call
sem_init(&semaphore, 1, 0)indicates that our semaphore is initially locked (the second parameter is 1), and is to be shared among the threads of a common process (the third parameter is 0). In contrast, mutexes always start out unlocked. It is important to note that in macOS, the equivalent function is
Destroy a semaphore using
main). This function only takes a pointer to the semaphore (
sem_destroy(&semaphore)). Note that in macOS, the equivalent function may be
sem_waitfunction indicates that a resource is being used, and decrements the semaphore. If the semaphore’s value is greater than 0 (indicating there are still resources available), the function will immediately return, and the thread is allowed to proceed. If the semaphore’s value is already 0, the thread will block until a resource becomes available (i.e., the semaphore has a positive value). A call to
sem_waittypically looks like
sem_postfunction indicates that a resource is being freed, and increments the semaphore. This function returns immediately. If there is a thread waiting on the semaphore (i.e., the semaphore’s value was previously 0), then the other thread will take ownership of the freed resource. A call to