A semaphore is a mechanism used for inter-process synchronization and mutual exclusion, widely applied in multithreaded programming and operating systems. Semaphores can control access to shared resources, ensuring that multiple processes or threads can orderly use these resources, avoiding race conditions.

1. What is a Semaphore?
✅ Definition:
A semaphore is a synchronization mechanism used to control access to shared resources by multiple threads or processes.
It is essentially a counter paired with two atomic operations:
- 1.
P operation (wait / acquire / down): Request resource, decrement the counter by 1. If the counter is 0, block and wait.
- 2.
V operation (signal / release / up): Release resource, increment the counter by 1. If other threads/processes are waiting for this resource, wake one of them up.
🎯 The main function of a semaphore is to achieve orderly access to shared resources, avoid race conditions, and solve synchronization and mutual exclusion problems.
2. Types of Semaphores (C Language Perspective)
In C language (especially in UNIX/Linux system programming), semaphores are generally divided into two main categories:
1. Binary Semaphore
- •
The counter has only two values: 0 or 1
- •
Functions similarly to a mutex, but does not require the thread that releases the lock to be the one that acquired it (this is different from Mutex)
- •
Used for mutual access to shared resources, ensuring that only one thread/process can enter the critical section at a time
2. Counting Semaphore
- •
The counter can be any non-negative integer (e.g., 0, 1, 2, …, N)
- •
Used to control multiple threads/processes accessing N identical resources simultaneously
- •
For example: In a thread pool with 5 database connections, a maximum of 5 threads can use them simultaneously
3. What Problems Can Semaphores Solve?
Semaphores are mainly used to solve the following typical concurrency problems:
|
Problem Type |
Description |
How Semaphores Solve It |
|---|---|---|
|
Mutual Access to Shared Resources |
Multiple threads/processes reading and writing the same variable/device, causing data inconsistency |
Use a binary semaphore (initially 1) to protect the critical section, allowing only one access at a time |
|
Resource Counting and Limiting |
For example, if there are 3 printers, how to limit a maximum of 3 threads using them simultaneously? |
Use a counting semaphore (initially 3), decrementing with P() for each resource used, and V() when done |
|
Thread/Process Synchronization |
For example, thread A must wait for thread B to complete initialization before executing |
Coordinate with semaphores, B completes and calls V(), A starts with P() |
|
Producer-Consumer Problem |
The producer puts data into a buffer, and the consumer takes data out; how to synchronize? |
Use two semaphores to control the number of empty and full slots |
|
Avoiding Race Conditions |
Multiple threads modifying shared data simultaneously leading to logical errors |
Protect the critical section with semaphores, ensuring only one thread operates at a time |
4. Semaphore Functions in UNIX/Linux C Language (System V or POSIX)
In C language system programming (Linux/Unix), semaphores are typically implemented in two ways:
1. ✅ POSIX Semaphores (recommended, more modern, commonly used for thread synchronization)
Header file:
#include <semaphore.h>
Main functions:
|
Function |
Description |
|---|---|
|
|
Define a semaphore variable |
|
|
Initialize the semaphore (0 indicates thread sharing, non-0 for process sharing) |
|
|
P operation: Request resource, decrement counter by 1, block if 0 |
|
|
V operation: Release resource, increment counter by 1, wake if waiting |
|
|
Destroy the semaphore |
✅ POSIX semaphores are suitable for thread synchronization (commonly used) and can also be used for inter-process (set the second parameter to non-0 & shared memory)
2. System V Semaphores (older, used for inter-process synchronization, slightly more complex)
Using <span>semget</span>, <span>semop</span>, <span>semctl</span>, etc., suitable for inter-process communication, but more complex than POSIX; here we will not elaborate, it is recommended to learn POSIX semaphores first.
5. Example of Using POSIX Semaphores in C
▶ Example 1: Using POSIX Semaphores to Implement Mutual Exclusion (Binary Semaphore, initially 1)
Problem: How to ensure thread safety when multiple threads modify a global variable?
#include <stdio.h>#include <pthread.h>#include <semaphore.h>#include <unistd.h>
int shared_data = 0;sem_t mutex; // Binary semaphore used as a mutex
void* thread_func(void* arg) { int id = *(int*)arg; for (int i = 0; i < 5; ++i) { sem_wait(&mutex); // P operation: enter critical section int tmp = shared_data; usleep(100000); // Simulate processing delay shared_data = tmp + 1; printf("Thread %d set shared_data = %d\n", id, shared_data); sem_post(&mutex); // V operation: leave critical section } return NULL;}
int main() { sem_init(&mutex, 0, 1); // Initialize semaphore, initial value is 1 (binary)
pthread_t t1, t2; int id1 = 1, id2 = 2;
pthread_create(&t1, NULL, thread_func, &id1); pthread_create(&t2, NULL, thread_func, &id2);
pthread_join(t1, NULL); pthread_join(t2, NULL);
sem_destroy(&mutex); // Destroy semaphore printf("Final shared_data = %d\n", shared_data); // Should be 10 return 0;}
🔒 Explanation:
- •
<span>sem_wait(&mutex)</span>is equivalent to locking, while<span>sem_post(&mutex)</span>is equivalent to unlocking. - •
Even if the lock is released by a different thread, it is allowed (this is different from mutex).
- •
This example achieves
<span>mutual access</span>to<span>shared_data</span>, solving the multithreading competition problem.
▶ Example 2: Using Semaphores to LimitOnly N Threads Can Access Resources Simultaneously (Counting Semaphore)
For example: Allow a maximum of 3 threads to execute a task simultaneously (e.g., using database connections)
#include <stdio.h>#include <pthread.h>#include <semaphore.h>#include <unistd.h>
#define MAX_THREADS 3
sem_t sem; // Counting semaphore, initial value is 3
void* task(void* arg) { int id = *(int*)arg; sem_wait(&sem); // P operation: request resource printf("Thread %d is using the resource...\n", id); sleep(2); // Simulate resource usage printf("Thread %d releases the resource\n", id); sem_post(&sem); // V operation: release resource return NULL;}
int main() { sem_init(&sem, 0, MAX_THREADS); // Initially allow 3 threads to access simultaneously
pthread_t threads[10]; int ids[10];
for (int i = 0; i < 10; ++i) { ids[i] = i + 1; pthread_create(&threads[i], NULL, task, &ids[i]); }
for (int i = 0; i < 10; ++i) { pthread_join(threads[i], NULL); }
sem_destroy(&sem); return 0;}
🔍 Explanation:
- •
A maximum of 3 threads can execute the task simultaneously, while others are blocked until resources are released.
- •
This simulates resource pools, connection pools, and rate limiting in real scenarios.
6. Typical Use Cases for Semaphores
|
Scenario |
Description |
Solution |
|---|---|---|
|
Multithreaded Mutual Access to Shared Data |
For example, multiple threads modifying the same global variable, file, or hardware device |
Use a binary semaphore (initially 1) to protect the critical section |
|
Limit Concurrent Access |
For example, database connection pools, API call rate limiting, thread pool task concurrency |
Use a counting semaphore (e.g., initially 5) to control a maximum of N threads accessing simultaneously |
|
Producer-Consumer Model |
The producer puts data into a buffer, and the consumer takes it out, requiring synchronization |
Use two semaphores to manage the number of empty and full slots |
|
Thread/Process Synchronization |
For example, thread A must wait for thread B to complete a certain operation |
Coordinate with semaphores, B calls V() after completion, A starts with P() |
|
Device/Hardware Access Control |
For example, multiple processes accessing exclusive devices like serial ports or printers |
Use semaphores to ensure only one access at a time |
7. Semaphores vs Mutexes
|
Feature |
Semaphore |
Mutex |
|---|---|---|
|
Essence |
Counter, can be multiple resources |
Lock, allows only one access at a time |
|
Initial Value |
Can be any non-negative number (e.g., 3) |
Can only be 1 (lock/unlock) |
|
Who Releases |
Any thread/process can release |
Must be released by the one who locked it |
|
Usage |
Mutual exclusion, resource counting, synchronization |
Mutual access to shared resources |
|
C Language Support |
POSIX: sem_t, or System V |
pthread_mutex_t |
|
Flexibility |
Higher, can implement complex synchronization logic |
More singular, used for mutual exclusion |
8. Summary: Key Points of Semaphores in C
|
Item |
Description |
|---|---|
|
What is a Semaphore? |
A counter + P/V operations used to control access to resources by multiple threads/processes |
|
P Operation (wait/acquire) |
Request resource, decrement counter by 1, block if 0 |
|
V Operation (signal/release) |
Release resource, increment counter by 1, wake waiting threads |
|
Binary Semaphore |
Initially 1, used for mutual exclusion (similar to Mutex) |
|
Counting Semaphore |
Initially N, used for resource pools / rate limiting |
|
How to Use in C? |
POSIX standard: |
|
What Problems Does It Solve? |
Mutual exclusion, synchronization, resource limitation, producer-consumer, thread coordination, etc. |
|
Applicable Scenarios |
Multithreaded programming, servers, embedded systems, operating systems, etc. |
✅ Recommended Learning Path:
- 1.
Understand Basic Concepts: P/V operations, binary vs counting semaphores
- 2.
Hands-on Practice: Write a few examples using POSIX semaphores (mutual exclusion, rate limiting)
- 3.
Solve Real Problems: For example, use semaphores to control access to shared resources, implement thread synchronization
- 4.
Expand Learning: Understand the producer-consumer model (using two semaphores)