Fundamentals 8 min read

Master Linux Synchronization: From Semaphores to Mutexes with Practical Code

Learn the core principles of synchronization and mutual exclusion in multi‑process or multi‑threaded Linux environments, explore design concepts such as atomic operations and deadlock avoidance, and see complete POSIX‑thread code examples using semaphores, condition variables, mutexes and spinlocks.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Master Linux Synchronization: From Semaphores to Mutexes with Practical Code

Synchronization

Synchronization coordinates multiple threads or processes so that they execute in a defined order or wait for specific conditions, guaranteeing correct access to shared resources.

Design principles

Atomic operations: Indivisible actions that either complete fully or not at all, forming the basis of safe concurrent execution.

Mutual exclusion: Guarantees that only one thread or process can access a shared resource at any given time.

Condition waiting: Allows a thread to block until a particular condition becomes true; another thread signals it to continue.

Order preservation: Controls execution order to meet expected sequencing requirements.

Linux implementation

Semaphores: Use sem_init, sem_wait and sem_post to count resources and limit concurrent access.

Condition variables: Use pthread_cond_init, pthread_cond_wait and pthread_cond_signal to block and wake threads based on conditions.

Mutex

A mutex (mutual exclusion lock) ensures exclusive access to a shared resource; only one thread may hold the lock at a time.

Design principles

Mutex lock: The lock itself that serializes access.

Critical section: The code region protected by the mutex where shared data is accessed.

Deadlock avoidance: Design the locking strategy to prevent circular‑wait situations.

Linux implementation

POSIX mutex: Functions pthread_mutex_init, pthread_mutex_lock and pthread_mutex_unlock manage the lock lifecycle.

Spinlock: Functions spin_lock and spin_unlock provide a busy‑wait lock that does not yield the CPU.

Example: Producer‑Consumer with a bounded buffer

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];
int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_producer = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_consumer = PTHREAD_COND_INITIALIZER;

void *producer(void *arg) {
    for (int i = 0; i < 10; ++i) {
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&cond_producer, &mutex);
        }
        buffer[count++] = i;
        printf("Produced: %d
", i);
        pthread_cond_signal(&cond_consumer);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

void *consumer(void *arg) {
    for (int i = 0; i < 10; ++i) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&cond_consumer, &mutex);
        }
        int item = buffer[--count];
        printf("Consumed: %d
", item);
        pthread_cond_signal(&cond_producer);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t producer_thread, consumer_thread;
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond_producer);
    pthread_cond_destroy(&cond_consumer);
    return 0;
}

Example: Incrementing a shared counter with two threads

#include <stdio.h>
#include <pthread.h>

int counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* increment_counter(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment_counter, NULL);
    pthread_create(&t2, NULL, increment_counter, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex);
    printf("Final Counter Value: %d
", counter);
    return 0;
}

Key points when using mutexes:

Initialize with PTHREAD_MUTEX_INITIALIZER or pthread_mutex_init.

Lock with pthread_mutex_lock before entering a critical section and unlock with pthread_mutex_unlock after leaving it.

Destroy the mutex with pthread_mutex_destroy when it is no longer needed.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LinuxSynchronizationmultithreadingmutexPOSIX
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.