Table of Contents

threads.h

POSIX pthreads work great on Linux but are not available everywhere. threads.h (C11) provides a portable threading API — thread creation, mutexes, condition variables, and thread-local storage — with a standardised interface that any conforming C11 implementation must provide.

The API surface:

Function Purpose
thrd_create(t, fn, arg) Create a thread running fn(arg)
thrd_join(t, res) Wait for thread, optionally get return value
thrd_exit(code) Exit current thread with a code
mtx_init, mtx_lock, mtx_unlock, mtx_destroy Mutex lifecycle
cnd_init, cnd_wait, cnd_signal, cnd_broadcast, cnd_destroy Condition variables
tss_create, tss_get, tss_set, tss_delete Thread-specific storage

Mutex types: mtx_plain (non-recursive), mtx_recursive (same thread can lock multiple times), mtx_timed (adds mtx_timedlock).

In practice, Linux programs tend to use pthreads directly or C++ <thread>, since <threads.h> support was absent from glibc until 2022 (version 2.36). For new portable C11 code it is the right choice; for existing Linux-only code, pthreads is more common.

Practice

// compile: gcc -o thrdemo thrdemo.c -lpthread
// run: ./thrdemo
// description: two threads increment a shared counter under a mutex
 
#include <threads.h>
#include <stdio.h>
 
mtx_t lock;
int   counter = 0;
 
int worker(void *arg) {
    (void)arg;
    for (int i = 0; i < 100000; i++) {
        mtx_lock(&lock);
        counter++;
        mtx_unlock(&lock);
    }
    return 0;
}
 
int main(void) {
    mtx_init(&lock, mtx_plain);
 
    thrd_t t1, t2;
    thrd_create(&t1, worker, NULL);
    thrd_create(&t2, worker, NULL);
 
    thrd_join(t1, NULL);
    thrd_join(t2, NULL);
 
    printf("counter: %d\n", counter);   // 200000
    mtx_destroy(&lock);
    return 0;
}

Remove the mtx_lock/mtx_unlock calls and run again — the counter will almost certainly land below 200000 due to the data race. This is the canonical demonstration of why shared mutable state needs synchronisation even for a single increment.