liveBook · Manning

Chapter 4: Chapter 4. Synchronizing concurrent operations

What you will learn in chapter 4 of C++ Concurrency in Action, Second Edition

The chapter introduces waiting for other threads’ actions and surveys the C++ facilities used to synchronize them.

After this chapter you will be able to

What it builds on

Section by section

Introduction

The chapter introduces waiting for other threads’ actions and surveys the C++ facilities used to synchronize them.

Key terms: shared data, synchronize actions, event, condition, condition variables, futures, Concurrency Technical Specification (TS), latches, barriers

4.1 Waiting for an event or other condition

This section compares ways for one thread to wait for another and introduces condition variables as the preferred way to wait for an event.

Key terms: flag, mutex, waiting thread, std::this_thread::sleep_for(), sleep period, condition variable, condition, notify

4.1.1 Waiting for a condition with condition variables

This section shows how a producer and consumer use a condition variable, a mutex, and a predicate to wait for queued data without holding the mutex while idle.

Key terms: std::condition_variable, std::condition_variable_any, std::mutex, std::lock_guard, std::unique_lock, wait(), notify_one(), data_queue, predicate, spurious wake

4.1.2 Building a thread-safe queue with condition variables

This section designs a thread-safe queue around condition variables, then explains how to notify one or all waiting threads and when a future may be a better fit.

Key terms: front(), pop(), try_pop(), wait_and_pop(), threadsafe_queue, mutex, condition variable, push(), mutable, notify_one()

4.2 Waiting for one-off events with futures

Introduces futures as a way to wait for one-off events and receive their results, then distinguishes unique, shared, and experimental futures.

Key terms: future, one-off event, ready, std::future<>, std::shared_future<>, <future>, std::future<void>, std::shared_future<void>, Concurrency TS, std::experimental::future<>

4.2.1 Returning values from background tasks

This section shows how std::async returns a future for a task’s result, how to pass arguments to the task, and how launch policies affect when it runs.

Key terms: std::async, std::future, get(), std::thread, pointer to a member function, std::ref, rvalues, move-only types, std::launch::deferred, std::launch::async

4.2.2 Associating a task with a future

This section shows how std::packaged_task connects a callable to a future and lets one thread submit work for another thread to run.

Key terms: std::packaged_task<>, get_future(), std::future<>, callable object, asynchronous result, function signature, return type, argument list, GUI thread, task queue

4.2.3 Making (std::)promises

This section explains how promises and futures let a small number of connection-handling threads deliver results to other threads waiting for network operations.

Key terms: std::promise<T>, std::future<T>, get_future(), set_value(), process_connections(), incoming packet, ID, payload, outgoing queue, std::promise<bool>

4.2.4 Saving an exception for the future

This section explains how futures deliver exceptions from asynchronous work, including explicitly stored exceptions and broken promises.

Key terms: std::async, std::packaged_task, std::future, get(), std::promise, set_exception(), set_value(), std::current_exception(), std::make_exception_ptr(), std::future_error

4.2.5 Waiting from multiple threads

The section explains how multiple threads can wait for one asynchronous result safely using std::shared_future, and how to create one from a std::future.

Key terms: std::future, std::shared_future, asynchronous state, get(), data race, lock, std::move, get_future(), share(), valid

4.3 Waiting with a time limit

This section introduces why a thread might limit a blocking wait, the two forms of timeout, and how condition-variable waits use them.

Key terms: blocking calls, timeout, duration-based timeout, absolute timeout, _for, _until, std::condition_variable, wait_for(), wait_until(), predicate

4.3.1 Clocks

This section explains what information a C++ clock provides and why a steady clock matters when measuring timeouts.

Key terms: now(), time_point, period, is_steady, std::ratio, steady clock, std::chrono::steady_clock, std::chrono::system_clock, std::chrono::high_resolution_clock, time_t

4.3.2 Durations

This section explains how C++ represents and manipulates durations and uses them to limit how long a thread waits.

Key terms: std::chrono::duration<>, representation, std::ratio, std::chrono::nanoseconds, std::chrono::milliseconds, std::chrono_literals, literal suffix operators, std::chrono::duration_cast<>, count(), truncated

4.3.3 Time points

This section explains how chrono time points represent absolute times and why a fixed deadline is useful for bounded waits.

Key terms: std::chrono::time_point<>, clock, duration, epoch, time_since_epoch(), now(), absolute timeout, clock parameter, steady clock, _until

4.3.4 Functions that accept timeouts

This section shows where C++ concurrency functions accept timeouts and how to choose between waiting for a duration and waiting until a time point.

Key terms: std::this_thread::sleep_for(), std::this_thread::sleep_until(), duration, time point, condition variables, futures, std::timed_mutex, std::recursive_timed_mutex, std::mutex, std::recursive_mutex

4.4 Using synchronization of operations to simplify code

This section introduces how synchronization facilities can simplify concurrent code by letting tasks receive the data they need and communicate results through futures instead of directly sharing data.

Key terms: synchronization facilities, functional programming, futures

4.4.1 Functional programming with futures

The section uses functional-style Quicksort to show how futures can coordinate parallel work without explicitly sharing mutable data, while noting the limits of task spawning.

Key terms: functional programming, pure function, shared data, race conditions, mutexes, Quicksort, pivot, std::partition, splice(), std::move()

4.4.2 Synchronizing operations with message passing

The section shows how message passing lets C++ threads act as independent state machines, using an ATM to illustrate the Actor model and its message handlers.

Key terms: CSP, message queues, shared data, state machine, Actor model, ATM logic, waiting_for_card, getting_pin, run(), wait()

4.4.3 Continuation-style concurrency with the Concurrency TS

This section introduces Concurrency TS futures and continuations, showing how to process a result when it becomes ready without explicitly waiting for it.

Key terms: std::experimental::future, continuation, then(), unspecified thread, invalid future, continuation result, chain continuations, ready future, std::experimental::future<int>, get()

4.4.4 Chaining continuations

This section shows how to chain future continuations so a login workflow can proceed asynchronously, propagate exceptions, and branch from a shared future.

Key terms: std::async, continuations, UI thread, std::experimental::future, .then(), .get(), exception, future-unwrapping, std::experimental::future<user_id>, async_authenticate_user

4.4.5 Waiting for more than one future

This section shows how to gather results from multiple asynchronous tasks without dedicating a thread to waiting for each future, and introduces waiting for the first result instead.

Key terms: std::async, future, context switches, std::experimental::when_all, .then, get, when_any, when_all

4.4.6 Waiting for the first future in a set with when_any

This section shows how to use std::experimental::when_any to react to the first completed future and keep checking parallel search results until one succeeds or all fail.

Key terms: std::experimental::when_any, std::experimental::when_any_result, index, continuation, DoneCheck, done_flag, final result, iterator-range overloads, variadic forms, vector

4.4.7 Latches and barriers in the Concurrency TS

This section distinguishes a one-shot latch from a reusable barrier by explaining how each lets threads wait for events or for one another.

Key terms: latch, counter, std::experimental::latch, barrier, cycle, arrive at the barrier

4.4.8 A basic latch type: std::experimental::latch

This section explains how a latch lets threads signal that required work is ready without requiring their tasks to finish, and how the latch makes their changes visible to a waiting thread.

Key terms: std::experimental::latch, <experimental/latch>, count_down, wait, is_ready, count_down_and_wait, std::async, std::future, latch, process_data

4.4.9 std::experimental::barrier: a basic barrier

This section explains how a reusable barrier coordinates a fixed group of threads across processing stages and illustrates where to place barrier waits in a data-processing loop.

Key terms: std::experimental::barrier, arrive_and_wait, arrive_and_drop, synchronization group, latch, Listing 4.26, source, sink, joining_thread, std::experimental::flex_barrier

4.4.10 std::experimental::flex_barrier—std::experimental::barrier’s flexible friend

This section shows how std::experimental::flex_barrier runs serial work at a barrier and can change the number of participating threads between cycles.

Key terms: std::experimental::flex_barrier, completion function, serial region, participating threads, next cycle, -1, split_source, synchronization point

4.5 Summary

The chapter summary explains why threads need synchronization, recaps the facilities and programming styles covered, and points ahead to the memory model and atomic operations.

Where this is used later

Concepts explained in this chapter

← Chapter 3: Chapter 3. Sharing data between threads Chapter 5: Chapter 5. The C++ memory model and operations on atomic types →

These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.