liveBook · Manning

Thread

As explained in Modern C

A thread is an independent sequence of execution within a program that has its own control flow and can execute concurrently or by interleaving with other threads.

Four tasks, five control flows

Imagine a terminal game whose cells change while the player moves a cursor. One task updates the cells, one counts their neighbors, one draws the screen, and one handles keystrokes. Give each task a thread and it follows its own control flow; the thread running main makes five in all. On multiple cores, threads may run simultaneously. Otherwise, their execution can be interleaved. Either way, drawing need not wait for a keystroke before the game continues. section 18.0

TAKEAWAY: A thread is a control flow within a running program, not a branch in the call structure of the thread that created it. In C, thrd_create starts a thread at a function with an int return type and a void* argument. It stores an identifier of type thrd_t; thrd_join uses that identifier to wait for termination. The game passes the same data object to four thread functions, so their separate control flows can act on shared state. section 17.0 section 18.0

201   /* Create an object that holds the game's data. */
202   life L = LIFE_INITIALIZER;
203   life_init(&L, n0, n1, M);
204   /* Creates four threads that all operate on that same object
205      and collects their IDs in "thrd" */
206   thrd_t thrd[4];
207   thrd_create(&thrd[0], update_thread,  &L);
208   thrd_create(&thrd[1], draw_thread,    &L);
209   thrd_create(&thrd[2], input_thread,   &L);
210   thrd_create(&thrd[3], account_thread, &L);
211   /* Waits for the update thread to terminate */
212   thrd_join(thrd[0], 0);
213   /* Tells everybody that the game is over */
214   L.finished = true;
215   ungetc('q', stdin);
216   /* Waits for the other threads */
217   thrd_join(thrd[1], 0);
218   thrd_join(thrd[2], 0);
219   thrd_join(thrd[3], 0);


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":3}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":3}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":3}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":3}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":3}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":3}],[{\"line\":5,\"ch\":6},{\"line\":5,\"ch\":12}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":3}],[{\"line\":6,\"ch\":6},{\"line\":6,\"ch\":17}],[{\"line\":7,\"ch\":6},{\"line\":7,\"ch\":17}],[{\"line\":8,\"ch\":6},{\"line\":8,\"ch\":17}],[{\"line\":9,\"ch\":6},{\"line\":9,\"ch\":17}],[{\"line\":7,\"ch\":0},{\"line\":7,\"ch\":3}],[{\"line\":6,\"ch\":6},{\"line\":6,\"ch\":17}],[{\"line\":7,\"ch\":6},{\"line\":7,\"ch\":17}],[{\"line\":8,\"ch\":6},{\"line\":8,\"ch\":17}],[{\"line\":9,\"ch\":6},{\"line\":9,\"ch\":17}],[{\"line\":8,\"ch\":0},{\"line\":8,\"ch\":3}],[{\"line\":6,\"ch\":6},{\"line\":6,\"ch\":17}],[{\"line\":7,\"ch\":6},{\"line\":7,\"ch\":17}],[{\"line\":8,\"ch\":6},{\"line\":8,\"ch\":17}],[{\"line\":9,\"ch\":6},{\"line\":9,\"ch\":17}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":3}],[{\"line\":6,\"ch\":6},{\"line\":6,\"ch\":17}],[{\"line\":7,\"ch\":6},{\"line\":7,\"ch\":17}],[{\"line\":8,\"ch\":6},{\"line\":8,\"ch\":17}],[{\"line\":9,\"ch\":6},{\"line\":9,\"ch\":17}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":3}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":3}],[{\"line\":11,\"ch\":6},{\"line\":11,\"ch\":15}],[{\"line\":16,\"ch\":6},{\"line\":16,\"ch\":15}],[{\"line\":17,\"ch\":6},{\"line\":17,\"ch\":15}],[{\"line\":18,\"ch\":6},{\"line\":18,\"ch\":15}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":3}],[{\"line\":13,\"ch\":0},{\"line\":13,\"ch\":3}],[{\"line\":13,\"ch\":19},{\"line\":13,\"ch\":23}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":3}],[{\"line\":14,\"ch\":6},{\"line\":14,\"ch\":12}],[{\"line\":14,\"ch\":18},{\"line\":14,\"ch\":23}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":3}],[{\"line\":16,\"ch\":0},{\"line\":16,\"ch\":3}],[{\"line\":11,\"ch\":6},{\"line\":11,\"ch\":15}],[{\"line\":16,\"ch\":6},{\"line\":16,\"ch\":15}],[{\"line\":17,\"ch\":6},{\"line\":17,\"ch\":15}],[{\"line\":18,\"ch\":6},{\"line\":18,\"ch\":15}],[{\"line\":17,\"ch\":0},{\"line\":17,\"ch\":3}],[{\"line\":11,\"ch\":6},{\"line\":11,\"ch\":15}],[{\"line\":16,\"ch\":6},{\"line\":16,\"ch\":15}],[{\"line\":17,\"ch\":6},{\"line\":17,\"ch\":15}],[{\"line\":18,\"ch\":6},{\"line\":18,\"ch\":15}],[{\"line\":18,\"ch\":0},{\"line\":18,\"ch\":3}],[{\"line\":11,\"ch\":6},{\"line\":11,\"ch\":15}],[{\"line\":16,\"ch\":6},{\"line\":16,\"ch\":15}],[{\"line\":17,\"ch\":6},{\"line\":17,\"ch\":15}],[{\"line\":18,\"ch\":6},{\"line\":18,\"ch\":15}]]"}
!@%STYLE%@!

The creation calls pass one shared object to four tasks; the joins show how main waits for each thread before finishing. section 18.0

Separate control flows make the lifetime of shared objects important. Initialize shared data before concurrent access, and never access it after destruction. An object created by main can be initialized before the other threads start. For static data that cannot be initialized until threads are running, call_once can run one initialization callback while other callers wait for it to finish. Prefer data local to a thread when sharing is unnecessary. section 18.2 section 18.7

Wait for work, then account for cells

The accounting thread locks a mutex and checks whether the game has finished or a new iteration needs counting. While neither permits work, it waits on a condition variable. It then counts neighbors in the protected critical section, signals the update thread, and unlocks the mutex. The update and draw threads use similar loops. TAKEAWAY: The condition variable identifies what a thread waits on; the condition expression must still be tested in a loop, because a wait can return without the needed work becoming available. section 18.5

Listing 18.2. The account thread function of B9
 99   int account_thread(void* Lv) {
100     life*restrict L = Lv;
101     while (!L->finished) {
102       // Blocks until there is work
103       mtx_lock(&L->mtx);
104       while (!L->finished && (L->accounted == L->iteration))
105         life_wait(&L->acco, &L->mtx);
106
107       // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
108       life_account(L);
109       if ((L->last + repetition) < L->accounted) {
110         L->finished = true;
111       }
112       // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
113
114       cnd_signal(&L->upda);
115       mtx_unlock(&L->mtx);
116     }
117     return 0;
118   }


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":1},{\"line\":0,\"ch\":3}],[{\"line\":0,\"ch\":6},{\"line\":0,\"ch\":9}],[{\"line\":0,\"ch\":25},{\"line\":0,\"ch\":29}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":3}],[{\"line\":1,\"ch\":13},{\"line\":1,\"ch\":21}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":3}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":10},{\"line\":5,\"ch\":15}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":3}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":3}],[{\"line\":4,\"ch\":10},{\"line\":4,\"ch\":18}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":3}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":10},{\"line\":5,\"ch\":15}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":3}],[{\"line\":7,\"ch\":0},{\"line\":7,\"ch\":3}],[{\"line\":8,\"ch\":0},{\"line\":8,\"ch\":3}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":3}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":3}],[{\"line\":1,\"ch\":9},{\"line\":1,\"ch\":11}],[{\"line\":6,\"ch\":13},{\"line\":6,\"ch\":15}],[{\"line\":9,\"ch\":11},{\"line\":9,\"ch\":13}],[{\"line\":10,\"ch\":10},{\"line\":10,\"ch\":12}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":3}],[{\"line\":11,\"ch\":26},{\"line\":11,\"ch\":30}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":3}],[{\"line\":13,\"ch\":0},{\"line\":13,\"ch\":3}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":3}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":3}],[{\"line\":15,\"ch\":10},{\"line\":15,\"ch\":20}],[{\"line\":16,\"ch\":0},{\"line\":16,\"ch\":3}],[{\"line\":16,\"ch\":10},{\"line\":16,\"ch\":20}],[{\"line\":17,\"ch\":0},{\"line\":17,\"ch\":3}],[{\"line\":18,\"ch\":0},{\"line\":18,\"ch\":3}],[{\"line\":18,\"ch\":8},{\"line\":18,\"ch\":14}],[{\"line\":19,\"ch\":0},{\"line\":19,\"ch\":3}]]"}
!@%STYLE%@!

Follow the accounting thread from its wait loop through the protected computation, signal, and unlock. section 18.5

Threads are not hierarchically organized: a thread identifier suffices to manage one. There is, however, a special consequence to ending main: returning from it or calling exit terminates all threads. If main must end while others continue, thrd_exit ends only that thread. A thread that will never be joined should be detached, so resources need not remain reserved for a future join. Shared data must still outlive the threads using it. section 18.6

How Modern C covers it

How deep it goes
Works through creation, joining, shared-data lifetime, condition-variable waits, and detachment using thread functions and game control flow.
Examples
Shows a terminal cell game with four worker threads sharing one object, plus accounting, update, and drawing loops.
What it assumes
Assumes familiarity with C functions and objects while explaining concurrent versus interleaved execution through a concrete task split.
What you can do after
Build it from scratch with C thread creation and joining, while planning shared-data lifetime and protected waits.
How it connects
Connects threads to control flow, shared data, critical sections, mutexes, condition variables, atomic objects, and thread-local storage.

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. The section links point to the parts of the book it draws on.