Semaphore Special Topic (C)

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.

Semaphore Special Topic (C)

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. 1.

    P operation (wait / acquire / down): Request resource, decrement the counter by 1. If the counter is 0, block and wait.

  2. 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

<span>sem_t sem;</span>

Define a semaphore variable

<span>sem_init(&sem, 0, initial_value);</span>

Initialize the semaphore (0 indicates thread sharing, non-0 for process sharing)

<span>sem_wait(&sem);</span>

P operation: Request resource, decrement counter by 1, block if 0

<span>sem_post(&sem);</span>

V operation: Release resource, increment counter by 1, wake if waiting

<span>sem_destroy(&sem);</span>

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: <span>sem_t</span> + <span>sem_init()</span>/ <span>sem_wait()</span>/ <span>sem_post()</span>/ <span>sem_destroy()</span>

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. 1.

    Understand Basic Concepts: P/V operations, binary vs counting semaphores

  2. 2.

    Hands-on Practice: Write a few examples using POSIX semaphores (mutual exclusion, rate limiting)

  3. 3.

    Solve Real Problems: For example, use semaphores to control access to shared resources, implement thread synchronization

  4. 4.

    Expand Learning: Understand the producer-consumer model (using two semaphores)

Leave a Comment