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











