liveBook · Manning

Thread

As explained in Modern C, Third Edition

A thread of execution is an independently schedulable sequence of program operations within a running program.

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

How Modern C, Third Edition covers it

How deep it goes
Explains creation, joining, shared-state hazards, initialization, and liveness; defers extensive deadlock treatment.
Examples
Uses a four-worker cell-matrix game and a two-mutex deadlock scenario.
What it assumes
Assumes familiarity with C functions and shared objects while introducing thread control and its safety rules through a concrete program.
What you can do after
Build it from scratch using thrd_create and thrd_join, then check shared access, object lifetime, and liveness.
How it connects
Connects threads to atomics, race conditions, mutexes, condition variables, and one-time initialization.

Also explained in

Compare how 12 books explain thread →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C, Third Edition. The section links point to the parts of the book it draws on.