A thread follows its own control flow within a running program. Threads let distinct tasks proceed concurrently: on multiple cores they may execute simultaneously; otherwise, their execution can be interleaved. Either way, the order in which their operations occur matters when they share data. section 20.0 section 19.0
Four tasks, one shared state
In a cell-matrix game, separate threads update cells, draw the display, handle input, and count neighboring cells. The main thread creates those four threads and passes each a pointer to the same game state. Each task has its own control flow, but none owns that shared state exclusively. section 20.0
/* Create an object that holds the game's data. */
life L = LIFE_INITIALIZER;
life_init(&L, n0, n1, M);
/* Creates four threads that all operate on that same object
and collects their IDs in "thrd" */
thrd_t thrd[4];
thrd_create(&thrd[0], update_thread, &L);
thrd_create(&thrd[1], draw_thread, &L);
thrd_create(&thrd[2], input_thread, &L);
thrd_create(&thrd[3], account_thread, &L);
/* Waits for the update thread to terminate */
thrd_join(thrd[0], nullptr);
/* Tells everybody that the game is over */
L.finished = true;
ungetc('q', stdin);
/* Waits for the other threads */
thrd_join(thrd[1], nullptr);
thrd_join(thrd[2], nullptr);
thrd_join(thrd[3], nullptr);
The listing shows thrd_create starting four task functions with &L as their shared argument. The subsequent thrd_join calls wait for those threads before the main thread finishes. section 20.0 section 20.1
TAKEAWAY: Starting threads also creates obligations around shared objects.
- Initialize shared data before concurrent access, and do not access it after destruction. For dynamic initialization that cannot happen before threads start, call_once can arrange for one callback to initialize it while other callers wait. section 20.2 section 20.2
- Protect potentially conflicting accesses. If one thread writes a non-atomic object while another reads or writes it simultaneously, execution fails. An _Atomic member can communicate a simple shared value, such as a finished flag. section 20.1 section 20.1
- Arrange for threads to finish. thrd_join waits for a specified thread; in the game, the main thread joins all four workers rather than ending while they still run. section 20.1
Race freedom is not the whole correctness story. Threads can deadlock when each waits for a mutex held by the other, or livelock by repeatedly cycling without progress. When a critical section needs several mutexes, acquire them in a consistent order. A timed condition wait is preferred over an indefinite wait to help avoid deadlock; ensuring liveness still requires reasoning about the program's control loops. section 20.7











