How to Use a Semaphore in C
- Use POSIX Semaphores to Synchronize Access to Shared Variables in C
-
Use the
sem_destroy
Function to Destroy Unnamed Semaphore
This article will demonstrate multiple methods about how to use a semaphore in C.
Use POSIX Semaphores to Synchronize Access to Shared Variables in C
There are two common semaphore APIs on UNIX-based systems - POSIX semaphores and System V semaphores. The latter is considered to have a less simple interface while offering the same features as POSIX API. Note that semaphores are yet another synchronization mechanism like mutexes and can be utilized in mostly similar scenarios. A semaphore is an integer maintained by the kernel, usually set to the initial value greater or equal to 0
.
Two operations can be done on a semaphore object - increment or decrement by one, which corresponds to acquiring and releasing the shared resource. POSIX provides a special sem_t
type for an unnamed semaphore, a more common tool in multi-threaded workflows. sem_t
variable must be initialized with the sem_init
function that also indicates whether the given semaphore should be shared between processes or threads of a process. Once the variable is initialized, we can implement the synchronization using the functions sem_post
and sem_wait
. sem_post
increments the semaphore, which usually corresponds to unlocking the shared resource. In contrast, sem_wait
decrements the semaphore and denotes the locking of the resource. Thus, the critical section would need to start with sem_wait
and end with sem_post
call. Mind though, that checking for success status code can be essential to debugging the code.
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
static long shared = 0;
static sem_t sem;
enum { THREADS = 4 };
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
static void *threadFunc(void *arg) {
long loops = *((long *)arg);
for (long j = 0; j < loops; j++) {
if (sem_wait(&sem) == -1) errExit("sem_wait");
shared++;
if (sem_post(&sem) == -1) errExit("sem_post");
}
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t t[THREADS];
int s;
long nloops;
if (argc != 2) {
fprintf(stderr, "Usage: %s num_loops\n", argv[0]);
exit(EXIT_FAILURE);
}
nloops = strtol(argv[1], NULL, 0);
if (sem_init(&sem, 0, 1) == -1) errExit("sem_init");
for (int i = 0; i < THREADS; ++i) {
s = pthread_create(&t[i], NULL, threadFunc, &nloops);
if (s != 0) errExit("pthread_create");
}
for (int i = 0; i < THREADS; ++i) {
s = pthread_join(t[i], NULL);
if (s != 0) errExit("pthread_join");
}
printf("shared = %ld\n", shared);
exit(EXIT_SUCCESS);
}
Sample Command:
./program_name 1000
Output:
shared = 4000
Use the sem_destroy
Function to Destroy Unnamed Semaphore
A semaphore initialized with a sem_init
call must be destroyed using the sem_destroy
function. Note though that sem_destroy
should be called when none of the processes/threads are waiting for it. Omitting the sem_destroy
call may result in a memory leak on some systems.
Generally, the semaphores have a similar performance compared to the Pthread mutexes, but the latter is usually preferred for better code structure. Although, there are some scenarios where the lock should be modified from the signal handler, which requires the function to be async-safe, and only sem_post
is implemented as such. There is also a named semaphore in POSIX API, that may persist even after a thread that created it and used it, terminates.
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
static long shared = 0;
static sem_t sem;
enum { THREADS = 4 };
#define errExit(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
static void *threadFunc(void *arg) {
long loops = *((long *)arg);
for (long j = 0; j < loops; j++) {
if (sem_wait(&sem) == -1) errExit("sem_wait");
shared++;
if (sem_post(&sem) == -1) errExit("sem_post");
}
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t t[THREADS];
int s;
long nloops;
if (argc != 2) {
fprintf(stderr, "Usage: %s num_loops\n", argv[0]);
exit(EXIT_FAILURE);
}
nloops = strtol(argv[1], NULL, 0);
if (sem_init(&sem, 0, 1) == -1) errExit("sem_init");
for (int i = 0; i < THREADS; ++i) {
s = pthread_create(&t[i], NULL, threadFunc, &nloops);
if (s != 0) errExit("pthread_create");
}
for (int i = 0; i < THREADS; ++i) {
s = pthread_join(t[i], NULL);
if (s != 0) errExit("pthread_join");
}
printf("shared = %ld\n", shared);
sem_destroy(&sem);
exit(EXIT_SUCCESS);
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook