Chapter 1: Chapter 1. Hello, world of concurrency in C++!
The chapter introduction explains why C++11’s standardized multithreading support mattered and previews the chapter’s path from basic concepts to a simple concurrent program.
After this chapter you will be able to
- Standardized C++ multithreading support. Explain that C++11 first added language recognition and standard-library components for multithreaded programs, making portable code with guaranteed behavior possible without platform-specific extensions. Describe C++14, C++17, and Technical Specifications as later additions to that support.
- Basic meaning of concurrency. Explain that concurrency means two or more separate activities happening at the same time, and illustrate it with an everyday example.
- Task switching and parallel execution. Distinguish a single core rapidly switching among independent tasks from hardware running tasks genuinely in parallel, while explaining why both count as concurrency.
- Hardware threads and excess tasks. Describe hardware threads as the measure of how many independent tasks hardware can genuinely run concurrently, and explain why task switching remains necessary when tasks outnumber that capacity.
- Separate process tradeoffs. Explain how an application can run concurrent single-threaded processes that communicate through channels such as pipes or sockets. Describe the tradeoff between process isolation, which can make code safer and allow work across machines, and the cost of communication and process management.
- Shared memory thread tradeoffs. Describe how independently running threads in one process share an address space and can communicate through shared data. Explain why this is relatively lightweight but requires the programmer to keep each thread's view of shared data consistent.
- Concurrency and parallelism distinction. Distinguish the terms by explaining that both concern running multiple tasks, but parallelism emphasizes using hardware to improve bulk-data performance, while concurrency emphasizes separation of concerns or responsiveness. State that this is a difference of focus, not a strict boundary.
- Reasons for using concurrency. State that applications chiefly use concurrency for separation of concerns or performance, and that other practical reasons generally reduce to one or both.
- Threads as concern boundaries. Explain how separate threads let related code stay together when distinct tasks must proceed at the same time. Describe how a DVD player can put playback and user-interface work in separate threads, with interaction at relevant points such as Pause rather than repeated input checks inside playback code.
- Multicore performance motivation. Explain why faster multicore hardware does not automatically make a single task run faster: its extra computing power comes from running work in parallel, so software must be designed for concurrency to use it.
- Single task parallel division. Describe how dividing one task into parallel parts can reduce its runtime, distinguishing different threads doing different processing from threads doing the same operation on different data. Explain why dependencies can make the division difficult and why readily divisible work can scale with available hardware threads.
- Throughput through parallel data. Distinguish using concurrency to process more data in a given time from using it to finish one task sooner. Describe how processing multiple independent data sets concurrently can increase throughput even when the time to process one chunk stays the same.
- Concurrency cost benefit decision. Explain why concurrency should be used only when its performance gains or clearer separation of concerns justify the extra development and maintenance costs. Describe how added complexity can make code harder to understand and more prone to bugs.
- Excessive thread count costs. Explain how too many concurrent threads can exhaust resources and reduce performance through context switching. Describe why the thread count should account for available hardware concurrency, using the thread-per-connection server as an example of a design that may not scale.
- C++ multithreading support history. Explain that before C++11, writing multithreaded C++ required platform-specific extensions, whereas C++11 introduced standardized support. Describe why this history helps explain decisions in the Standard C++ Thread Library.
- Prestandard threading limitations. Explain why the 1998 C++ Standard could not, by itself, specify multithreaded programs: it described a sequential abstract machine, did not acknowledge threads, and had no formally defined memory model. Describe why the missing standard guarantees particularly affected hardware-aware optimizations and cross-platform code.
- C++11 concurrency facilities. Describe how C++11 added a thread-aware memory model and Standard Library facilities for managing threads, protecting shared data, synchronizing operations, and performing low-level atomic operations.
- C++14 and C++17 additions. Distinguish the concurrency support added by C++14 from that added by C++17: C++14 added one new mutex type, while C++17 added a full suite of parallel algorithms. Describe how broader language and library enhancements in both standards can simplify multithreaded code.
- Portable atomic operation benefits. Explain why direct C++ support for atomic operations helps programmers write efficient, portable code with defined semantics: it avoids platform-specific assembly, lets the compiler handle platform details, and gives the optimizer operation semantics it can use.
- Thread library abstraction penalty. Explain that the abstraction penalty is the cost of using a high-level facility instead of an equivalent low-level one, and that the C++ Thread Library was designed to keep that penalty low on major platforms.
- Portable low-level atomic operations. Describe how the memory model and atomic operations library provide low-level control over data changes and their synchronization and visibility between threads, often replacing platform-specific assembly with more portable, maintainable code.
- Performance-driven facility selection. Distinguish the cost of extra high-level functionality from an abstraction penalty, and explain why profiling and reducing contention may be better than replacing library facilities. Describe when hand-built lower-level or platform-specific facilities may nevertheless be warranted.
- Platform-specific access through native handles. Explain that C++ Thread Library types may provide native_handle() to let code manipulate their underlying implementation through a platform-specific API. State that such operations are platform dependent and that the Standard Library’s facilities should be understood before considering them.
- Thread management and shared data. Explain that running functions concurrently requires specific functions and objects to manage threads, and that shared data must be safe for concurrent access.
- Launching the hello thread. Trace how the example uses <thread>, a separate hello() function, and a std::thread object to print the message on a new thread rather than directly from main().
- Waiting with thread join. Explain why main() calls t.join() after starting the new thread and what could happen if it did not wait.
Section by section
Introduction
The chapter introduction explains why C++11’s standardized multithreading support mattered and previews the chapter’s path from basic concepts to a simple concurrent program.
- Standardized C++ multithreading support: Explain that C++11 first added language recognition and standard-library components for multithreaded programs, making portable code with guaranteed behavior possible without platform-specific extensions. Describe C++14, C++17, and Technical Specifications as later additions to that support.
Why it matters: This is the starting point for understanding which concurrency facilities the book uses and why standard support matters. - Chapter learning path: Describe the chapter’s planned progression: define concurrency and multithreading, consider reasons to use or avoid concurrency, survey C++ support, and finish with a simple concurrent C++ example.
Why it matters: The roadmap helps the reader place the chapter’s concepts and example in context.
Key terms: C++11, C++14, C++17, Technical Specifications, platform-specific extensions, portable multithreaded code, concurrency, multithreading, C++ concurrency support, simple example
1.1 What is concurrency?
The section introduces concurrency as two or more separate activities happening at the same time, using everyday examples.
- Basic meaning of concurrency: Explain that concurrency means two or more separate activities happening at the same time, and illustrate it with an everyday example.
Why it matters: This everyday meaning is the starting point for discussing concurrency in computer systems.
Key terms: concurrency, separate activities
1.1.1 Concurrency in computer systems
This section distinguishes task-switched concurrency from genuine hardware concurrency and explains how hardware capacity affects execution and design.
- Task switching and parallel execution: Distinguish a single core rapidly switching among independent tasks from hardware running tasks genuinely in parallel, while explaining why both count as concurrency.
Why it matters: Concurrent tasks can appear to run together even when the hardware executes only one at a time. - Context switch costs: Explain what the operating system does when switching tasks and why interleaving them can introduce delays.
Why it matters: Switching tasks takes time that could otherwise be spent executing them. - Hardware threads and excess tasks: Describe hardware threads as the measure of how many independent tasks hardware can genuinely run concurrently, and explain why task switching remains necessary when tasks outnumber that capacity.
Why it matters: Even a multicore computer often has more runnable tasks than it can execute in parallel. - Hardware independent concurrency tools: Explain that the book’s concurrency techniques, functions, and classes apply on single-core and multicore machines, while application design may depend on available hardware concurrency.
Why it matters: The same tools work across machines, but the best way to use concurrency can vary.
Key terms: concurrency, task switching, hardware concurrency, context switch, CPU state, instruction pointer, cache, hardware threads, multicore processors
1.1.2 Approaches to concurrency
This section compares concurrency using separate processes with concurrency using threads in one process, then explains why the book focuses on threads.
- Separate process tradeoffs: Explain how an application can run concurrent single-threaded processes that communicate through channels such as pipes or sockets. Describe the tradeoff between process isolation, which can make code safer and allow work across machines, and the cost of communication and process management.
Why it matters: Process isolation changes both how concurrent work communicates and what can go wrong. - Shared memory thread tradeoffs: Describe how independently running threads in one process share an address space and can communicate through shared data. Explain why this is relatively lightweight but requires the programmer to keep each thread's view of shared data consistent.
Why it matters: Easy access to shared data is useful, but it creates the problems that much of the book addresses. - C++ multithreading focus: Justify why the book uses multithreading as its approach to concurrency: threads have comparatively low launch and communication overhead, while the C++ Standard provides no intrinsic interprocess communication support. State that later references to concurrency in the book assume multiple threads.
Why it matters: This sets the scope for what the book means when it discusses concurrency.
Key terms: processes, interprocess communication, signals, sockets, files, pipes, threads, shared address space, shared memory, consistent
1.1.3 Concurrency vs. parallelism
This section distinguishes concurrency from parallelism by the goals people emphasize when using the terms, while stressing that their meanings overlap.
- Concurrency and parallelism distinction: Distinguish the terms by explaining that both concern running multiple tasks, but parallelism emphasizes using hardware to improve bulk-data performance, while concurrency emphasizes separation of concerns or responsiveness. State that this is a difference of focus, not a strict boundary.
Why it matters: The distinction helps clarify why an application uses multiple tasks, even though the terms often overlap.
Key terms: concurrency, parallelism, separation of concerns, responsiveness, bulk data processing
1.2 Why use concurrency?
This section identifies separation of concerns and performance as the two main reasons to use concurrency.
- Reasons for using concurrency: State that applications chiefly use concurrency for separation of concerns or performance, and that other practical reasons generally reduce to one or both.
Why it matters: These two reasons provide a way to judge what concurrency is meant to achieve in an application.
Key terms: separation of concerns, performance
1.2.1 Using concurrency for separation of concerns
The section shows how assigning distinct responsibilities to threads can simplify code and keep an application responsive, even when increasing throughput is not the aim.
- Threads as concern boundaries: Explain how separate threads let related code stay together when distinct tasks must proceed at the same time. Describe how a DVD player can put playback and user-interface work in separate threads, with interaction at relevant points such as Pause rather than repeated input checks inside playback code.
Why it matters: Separating responsibilities makes each task’s logic easier to understand and test. - Responsive interface behavior: Describe how a user-interface thread can respond promptly while another thread does work, even if its immediate response is only a busy cursor or Please Wait message. Give background filesystem monitoring as another example of a distinct ongoing task.
Why it matters: An application can acknowledge a request promptly without having finished the requested work. - Design driven thread count: Distinguish dividing an application into threads by responsibility from choosing threads to increase throughput. State that, for separation of concerns, the number of threads is independent of the number of CPU cores.
Why it matters: The right division of responsibilities need not be dictated by the machine’s core count.
Key terms: separation of concerns, multithreading, user interface, DVD playback, responsiveness, busy cursor, Please Wait, background, number of threads, CPU cores
1.2.2 Using concurrency for performance: task and data parallelism
The section explains why multicore hardware makes concurrency important for performance and distinguishes reducing a task’s runtime from processing more data in the same time.
- Multicore performance motivation: Explain why faster multicore hardware does not automatically make a single task run faster: its extra computing power comes from running work in parallel, so software must be designed for concurrency to use it.
Why it matters: This explains why concurrency became a practical performance concern even for ordinary desktop and embedded software. - Single task parallel division: Describe how dividing one task into parallel parts can reduce its runtime, distinguishing different threads doing different processing from threads doing the same operation on different data. Explain why dependencies can make the division difficult and why readily divisible work can scale with available hardware threads.
Why it matters: The way a task is divided determines both whether it can run correctly in parallel and how much speedup it can gain. - Throughput through parallel data: Distinguish using concurrency to process more data in a given time from using it to finish one task sooner. Describe how processing multiple independent data sets concurrently can increase throughput even when the time to process one chunk stays the same.
Why it matters: Greater throughput can enable larger workloads even without reducing the time each individual chunk takes.
Key terms: multicore, parallel, concurrency, The free lunch is over, task parallelism, data parallelism, embarrassingly parallel, scalability, dependencies, throughput
1.2.3 When not to use concurrency
The section explains when the costs of concurrency outweigh its benefits, including development complexity, thread startup overhead, and the limits of running many threads.
- Concurrency cost benefit decision: Explain why concurrency should be used only when its performance gains or clearer separation of concerns justify the extra development and maintenance costs. Describe how added complexity can make code harder to understand and more prone to bugs.
Why it matters: The decision to use concurrency starts with whether its benefits outweigh its costs. - Thread startup overhead: Explain why putting a short task on a new thread can make an application slower than executing it in the spawning thread. Describe the operating-system work involved in launching a thread.
Why it matters: Thread creation has a cost that may exceed the work a short task performs. - Excessive thread count costs: Explain how too many concurrent threads can exhaust resources and reduce performance through context switching. Describe why the thread count should account for available hardware concurrency, using the thread-per-connection server as an example of a design that may not scale.
Why it matters: Adding threads eventually stops helping and can make both the application and the system slower.
Key terms: separation of concerns, multithreaded code, measurable gain, launching a thread, kernel resources, stack space, scheduler, spawning thread, limited resource, address space
1.3 Concurrency and multithreading in C++
Introduces C++11 as the point when C++ gained standardized multithreading support and explains why the library’s history matters.
- C++ multithreading support history: Explain that before C++11, writing multithreaded C++ required platform-specific extensions, whereas C++11 introduced standardized support. Describe why this history helps explain decisions in the Standard C++ Thread Library.
Why it matters: The shift to standardized support provides context for the library’s design choices.
Key terms: C++11 Standard, platform-specific extensions, Standard C++ Thread Library
1.3.1 History of multithreading in C++
This section explains how C++ programmers built multithreaded applications before standard thread support, and why the lack of a standard memory model remained a problem.
- Prestandard threading limitations: Explain why the 1998 C++ Standard could not, by itself, specify multithreaded programs: it described a sequential abstract machine, did not acknowledge threads, and had no formally defined memory model. Describe why the missing standard guarantees particularly affected hardware-aware optimizations and cross-platform code.
Why it matters: These limitations explain why standard concurrency support was needed despite the existence of multithreaded C++ programs. - Platform APIs and library wrappers: Describe how compiler extensions allowed C++ programs to use platform-specific C threading APIs, while C++ libraries such as Boost and ACE wrapped underlying APIs in higher-level classes.
Why it matters: This explains how substantial multithreaded C++ software existed before threading was standardized. - RAII locking benefit: Explain how C++ class libraries used Resource Acquisition Is Initialization (RAII) with locks so that a mutex is unlocked when the relevant scope is exited.
Why it matters: Scope-based unlocking is a recurring way to make lock management safer.
Key terms: 1998 C++ Standard, sequential abstract machine, memory model, compiler-specific extensions, platform-specific extensions, POSIX C, Microsoft Windows API, C++ Runtime Library, Boost, ACE
1.3.2 Concurrency support in the C++11 standard
The section introduces C++11’s standard concurrency support, its roots in Boost Thread, and the relevance of other C++11 language changes.
- C++11 concurrency facilities: Describe how C++11 added a thread-aware memory model and Standard Library facilities for managing threads, protecting shared data, synchronizing operations, and performing low-level atomic operations.
Why it matters: These facilities provide the foundation for the concurrency techniques developed in later chapters. - Boost Thread influence: Explain that the C++11 Thread Library drew heavily on earlier C++ concurrency libraries, especially Boost Thread, and that Boost subsequently changed to match the C++ Standard in many respects.
Why it matters: The shared names and structure help explain why Boost Thread users may find the standard library familiar. - Related C++11 language changes: State that C++11 also introduced broader language enhancements, some of which directly affect the Thread Library and how it can be used, although they are generally outside this book’s scope.
Why it matters: Thread-library code may rely on language features introduced alongside the concurrency facilities.
Key terms: thread-aware memory model, C++ Standard Library, threads, shared data, synchronizing operations, atomic operations, Boost Thread Library, C++11 Thread Library, C++11 Standard, Thread Library
1.3.3 More support for concurrency and parallelism in C++14 and C++17
This section contrasts the concurrency additions in C++14 and C++17, notes the Concurrency Technical Specification, and explains why C++ atomic operations support efficient, portable code.
- C++14 and C++17 additions: Distinguish the concurrency support added by C++14 from that added by C++17: C++14 added one new mutex type, while C++17 added a full suite of parallel algorithms. Describe how broader language and library enhancements in both standards can simplify multithreaded code.
Why it matters: The two standards added different kinds of support, so it helps to keep their contributions distinct. - Concurrency Technical Specification extensions: Describe the concurrency Technical Specification as a description of extensions to Standard C++ functions and classes, especially for synchronizing operations between threads.
Why it matters: It points to additional synchronization facilities beyond the standard features listed here. - Portable atomic operation benefits: Explain why direct C++ support for atomic operations helps programmers write efficient, portable code with defined semantics: it avoids platform-specific assembly, lets the compiler handle platform details, and gives the optimizer operation semantics it can use.
Why it matters: This explains the value of standard atomics beyond simply having another concurrency API.
Key terms: C++14, C++17, mutex type, parallel algorithms, Technical Specification for concurrency, synchronizing operations between threads, atomic operations, defined semantics, platform-specific assembly language, optimizer
1.3.4 Efficiency in the C++ Thread Library
This section explains the performance goals of the C++ Thread Library and when lower-level facilities may be worth using.
- Thread library abstraction penalty: Explain that the abstraction penalty is the cost of using a high-level facility instead of an equivalent low-level one, and that the C++ Thread Library was designed to keep that penalty low on major platforms.
Why it matters: A library call is not necessarily slower than doing the equivalent work directly. - Portable low-level atomic operations: Describe how the memory model and atomic operations library provide low-level control over data changes and their synchronization and visibility between threads, often replacing platform-specific assembly with more portable, maintainable code.
Why it matters: The standard offers a way to work close to the hardware without immediately resorting to platform-specific code. - Performance-driven facility selection: Distinguish the cost of extra high-level functionality from an abstraction penalty, and explain why profiling and reducing contention may be better than replacing library facilities. Describe when hand-built lower-level or platform-specific facilities may nevertheless be warranted.
Why it matters: Replacing a library facility can add complexity and errors without fixing the actual bottleneck.
Key terms: abstraction penalty, C++ Standard Library, C++ Standard Thread Library, memory model, atomic operations library, atomic types, inter-thread synchronization, visibility, high-level abstractions, profiling
1.3.5 Platform-specific facilities
This section explains how C++ Thread Library types can provide access to platform-specific facilities, while recommending that readers learn the standard facilities first.
- Platform-specific access through native handles: Explain that C++ Thread Library types may provide native_handle() to let code manipulate their underlying implementation through a platform-specific API. State that such operations are platform dependent and that the Standard Library’s facilities should be understood before considering them.
Why it matters: This distinction helps you recognize when code goes beyond the portable facilities provided by the C++ Standard Library.
Key terms: C++ Thread Library, native_handle(), platform-specific API
1.4 Getting started
This section introduces the shape of a multithreaded C++ program and the two new concerns: managing threads and safely accessing shared data.
- Concurrent functions in ordinary programs: Describe how a multithreaded C++ program still uses ordinary variables, classes, and functions, but may run some functions concurrently.
Why it matters: Concurrency changes how parts of a program run, not its basic C++ structure. - Thread management and shared data: Explain that running functions concurrently requires specific functions and objects to manage threads, and that shared data must be safe for concurrent access.
Why it matters: These are the two practical concerns the book develops after this introduction.
Key terms: functions, concurrently, threads, shared data, concurrent access
1.4.1 Hello, Concurrent World
The section turns a single-threaded Hello World program into a two-thread example, then explains why it must wait for the new thread and why this simple task gains little from concurrency.
- Launching the hello thread: Trace how the example uses <thread>, a separate hello() function, and a std::thread object to print the message on a new thread rather than directly from main().
Why it matters: This example shows the basic pieces needed to start a thread in C++. - Waiting with thread join: Explain why main() calls t.join() after starting the new thread and what could happen if it did not wait.
Why it matters: Starting a thread is not enough if the program can finish before that thread runs. - Limited value of this example: Justify why printing one message is generally not worth doing on a separate thread, especially when main() has no other work to do.
Why it matters: The example teaches thread mechanics, not a useful performance improvement.
Key terms: <thread>, std::thread, initial function, hello(), main(), join(), calling thread, multiple threads, initial thread
1.5 Summary
The chapter recap connects C++’s growing concurrency support with multicore hardware, then distinguishes the ease of starting threads from the difficulty of designing correct concurrent code.
Where this is used later
- Oversubscription and excessive task switching (chapter 8) · Provides the basic source of excessive task-switching costs.
- Choosing the number of threads at runtime (chapter 2) · Relates available hardware concurrency to choosing a thread count.
- How many processors? (chapter 8) · Considers processor availability when designing for performance.
- Problems with sharing data between threads (chapter 3) · Introduces problems caused by sharing data between threads.
- Objects, memory locations, and concurrency (chapter 5) · Examines objects and memory locations under concurrent access.
- Dividing work by task type (chapter 8) · Dividing work by task type builds on assigning distinct responsibilities to separate threads.
- Synchronizing operations with message passing (chapter 4) · Using separate threads to separate concerns.
- Factors affecting the performance of concurrent code (chapter 8) · Introduces using concurrency to separate concerns.
Concepts explained in this chapter
- concurrencyConcurrency is the structuring and execution of multiple tasks whose progress overlaps, whether or not they run at the exact same time.
- multithreadingA programming technique in which a program uses multiple threads of execution within a process.
- threadA unit of execution within a process that can run independently of other threads.
- hardware concurrencyHardware concurrency is the degree of parallel execution supported by the available hardware.
- parallelismParallelism is the simultaneous execution of multiple tasks, typically to use available hardware and improve performance.
- task switchingA scheduling technique in which a processor alternates execution among tasks, running part of each in turn.
- C++ multithreading libraryThe C++ standard library facilities for creating and managing threads and coordinating concurrent execution.
- thread-aware memory modelA programming-language memory model that defines how operations on memory behave and become visible across threads.
- separation of concernsSeparation of concerns is a software design principle that organizes a system so distinct concerns are handled independently.
- resource acquisition is initializationResource Acquisition Is Initialization is a C++ programming idiom that binds resource ownership and release to object lifetime.
- atomic operationAn atomic operation is an operation that appears indivisible to other threads under the C++ memory model.
- std::threadA C++ standard library class that represents and manages a thread of execution.
- mutexA synchronization primitive used to coordinate exclusive access to a shared resource.
- parallel algorithmAn algorithm that can distribute its work across multiple execution agents.
- thread poolA thread pool is a managed collection of reusable threads that execute submitted tasks.
- scalabilityScalability is the ability of a system or algorithm to increase its capacity as available resources increase.
- concurrent accessConcurrent access is access to the same data by multiple threads during overlapping periods of execution.
- mutable shared stateShared mutable data is information accessible by multiple threads that can also be modified during execution.
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.
