This section introduces the fundamental tool for thread synchronization: the Mutex (short for Mutual Exclusion).

Core Objective: Protecting the “Critical Region”

The primary goal of a mutex is to protect a critical region. A critical region is a block of code that accesses or modifies shared data (like a global counter or a linked list).

  • Without a Mutex: Multiple threads might enter this region at the same time, overwriting each other’s changes (the “race condition” we discussed in Volume 1).
  • With a Mutex: We ensure that only one thread executes the code within the critical region at any given time.
1. The Code Pattern

The logic is simple and must always follow this pattern:

lock_the_mutex(...);
 
/* CRITICAL REGION */
/* Access shared data here */
 
unlock_the_mutex(...);

Since only one thread can hold the lock at a time, if a second thread tries to lock it while the first thread is inside, the second thread is forced to wait (block) until the first thread unlocks it.

2. The API Functions

Stevens introduces the three basic functions required to manipulate a mutex. A. Locking and Unlocking

#include <pthread.h>
 
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr);
  • pthread_mutex_lock: If the mutex is unlocked, it locks it and returns immediately. If the mutex is already locked by another thread, this function blocks (sleeps) the calling thread until the mutex becomes available.
  • pthread_mutex_unlock: Releases the lock, allowing a waiting thread to proceed.
B. Non-Blocking Lock (trylock)
int pthread_mutex_trylock(pthread_mutex_t *mptr);
  • This function never puts the thread to sleep.
  • If the mutex is free, it locks it.
  • If the mutex is already locked, it returns an error EBUSY immediately.

Note

This is useful if you want your thread to go do other work instead of waiting.

3. Initialization

Before you can use a mutex, it must be initialized. The book distinguishes between static and dynamic allocation:

  • Static Initialization: If your mutex is a global variable or a static variable, you can initialize it with a special constant:
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
This is the most common way for simple programs.
  • Dynamic Initialization: If you allocate a mutex using malloc (or in shared memory), you must call pthread_mutex_init (which we will cover in Section 7.7). Note: Omitting initialization is a common bug!
4. Important Concept: “Cooperative Locks”

Stevens emphasizes that mutexes are cooperative locks.

  • Meaning: The mutex prevents access only if all threads agree to check the lock before touching the data.
  • The Danger: If Thread A locks the mutex to update a variable, but Thread B writes to that variable without checking the lock first, the system cannot stop it. The code is “broken” because the threads are not cooperating.