Binary Semaphore
Mutex
Computer Science
Programming Concepts
Concurrency Control

Difference between binary semaphore and mutex

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Binary semaphores and mutexes can both make one execution path wait while another proceeds, which is why they are often confused. The important difference is intent. A mutex is primarily about ownership and protecting a critical section. A binary semaphore is usually about signaling that some condition or event has occurred.

A Mutex Protects Shared State

A mutex enforces mutual exclusion. One thread locks it, enters the critical section, and later unlocks it. In many runtimes, the thread that unlocks the mutex is expected to be the same thread that acquired it.

c
1#include <pthread.h>
2#include <stdio.h>
3
4pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
5int counter = 0;
6
7void increment_counter(void) {
8    pthread_mutex_lock(&lock);
9    counter += 1;
10    pthread_mutex_unlock(&lock);
11}

This pattern is about protecting shared mutable state. The mutex does not represent data readiness. It represents exclusive access to something that would be unsafe if touched concurrently.

A Binary Semaphore Models Availability or Notification

A binary semaphore has only two meaningful states, available or unavailable. Unlike a mutex, it does not inherently express ownership. One thread can wait on it and another thread can signal it.

c
1#include <pthread.h>
2#include <semaphore.h>
3
4sem_t ready;
5
6void producer(void) {
7    sem_post(&ready);
8}
9
10void consumer(void) {
11    sem_wait(&ready);
12    /* continue after producer signals */
13}

This is why semaphores are commonly used for producer-consumer coordination, wake-up signals, and event notification between tasks that are not sharing a lock ownership model.

Ownership Is the Practical Difference

When reviewing code, the question to ask is simple: are you protecting a critical section, or are you signaling another execution path. If the problem is "only one thread may modify this map at a time," a mutex is the natural tool. If the problem is "consumer must wait until producer finishes setup," a binary semaphore is often a better fit.

That difference affects correctness. Using a binary semaphore to guard shared state can make misuse easier because another thread can signal it without actually owning the critical section. Using a mutex for cross-thread signaling can make the control flow awkward and error-prone because the waiting thread and the notifying thread do not share the same ownership semantics.

Scheduling and Runtime Behavior Matter

Some platforms add mutex features such as priority inheritance or error checking. Those features matter in real-time or latency-sensitive systems because they reduce certain classes of concurrency bugs. Binary semaphores are generally simpler. They may be perfectly correct for event signaling, but they do not necessarily provide the same protection or diagnostics that a mutex does.

Another difference is intent clarity. A future maintainer who sees a mutex expects a protected region. A maintainer who sees a semaphore expects coordination or pacing. Choosing the right primitive helps humans understand the synchronization contract just as much as it helps the scheduler.

Use Cases Side by Side

A short rule of thumb often helps:

  • Use a mutex when multiple threads touch the same mutable resource.
  • Use a binary semaphore when one task must block until another task says "go."

For example, a cache update path should usually use a mutex. A worker that waits for a hardware event or a background task that sleeps until new work arrives may be better modeled with a semaphore.

The APIs can look similar enough to fool beginners, but the underlying contract is different. That is why naming and documentation matter in concurrency code.

Common Pitfalls

The usual mistake is choosing by familiarity rather than by synchronization model. Teams also misuse binary semaphores as poor substitutes for locks, or they force mutexes into signaling workflows that really want a producer-consumer primitive. Another recurring problem is ignoring runtime-specific behavior such as fairness, priority handling, or timeout semantics and assuming all implementations behave alike.

Summary

  • A mutex protects a critical section through ownership-based locking.
  • A binary semaphore usually signals availability or completion.
  • Ownership is expected with mutexes and usually not required with semaphores.
  • Choose the primitive based on whether you are protecting state or coordinating tasks.
  • Clear synchronization intent reduces both bugs and maintenance cost.

Course illustration
Course illustration

All Rights Reserved.