liveBook · Manning

Chapter 6: Chapter 6. Designing lock-based concurrent data structures

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

The chapter introduces why data structures need deliberate design for concurrent access and previews lock-based examples before a later chapter turns to lock-free designs.

After this chapter you will be able to

What it builds on

Section by section

Introduction

The chapter introduces why data structures need deliberate design for concurrent access and previews lock-based examples before a later chapter turns to lock-free designs.

Key terms: immutable, synchronization, external locking, concurrent access, locks, condition variables, atomic operations, lock-free

6.1 What does it mean to design for concurrency?

Defines thread safety for a concurrent data structure and explains why designing for concurrency also means limiting serialization.

Key terms: thread-safe, self-consistent view, invariants, problematic race conditions, mutex, mutual exclusion, serialization, protected region, true concurrent access

6.1.1 Guidelines for designing data structures for concurrency

This section gives designers a checklist for making concurrent data structures safe while allowing as much simultaneous access as their operations permit.

Key terms: invariants, race conditions inherent in the interface, exceptions, deadlock, nested locks, constructors, destructors, assignment, swap(), copy construction

6.2 Lock-based concurrent data structures

This section introduces the safety and concurrency tradeoffs in lock-based data structures before examining a stack and other simple examples.

Key terms: lock-based concurrent data structures, mutex, race conditions inherent in the interface, thread-safe, separate mutexes, deadlock, multiple mutexes, stack, single mutex, true concurrency

6.2.1 A thread-safe stack using locks

The section examines how a mutex-protected stack handles races, exceptions, and concurrent access, then explains its deadlock risks and limitations under contention or when callers need to wait for data.

Key terms: mutex, std::lock_guard<>, empty(), pop(), top(), invariants, std::make_shared, std::shared_ptr, empty_stack, data.push()

6.2.2 A thread-safe queue using locks and condition variables

This section explains how a mutex-and-condition-variable queue handles empty queues, where its exception-safety and concurrency limits arise, and how storing shared pointers changes those trade-offs.

Key terms: try_pop(), wait_and_pop(), empty(), condition variable, data_cond.wait(), mutex, invariants, data_cond.notify_one(), data_cond.notify_all(), std::shared_ptr<>

6.2.3 A thread-safe queue using fine-grained locks and condition variables

This section builds a fine-grained, lock-based queue and explains how its dummy node, mutexes, and condition variable preserve correctness while allowing concurrent pushes and pops.

Key terms: head, tail, dummy node, std::unique_ptr<node>, std::shared_ptr<>, head_mutex, tail_mutex, get_tail(), pop_head(), queue invariants

6.3 Designing more complex lock-based data structures

This section introduces why lock-based designs become harder as data structures support more operations, then points to a lookup table as an example.

Key terms: stacks and queues, operations, access patterns, concurrent access, lookup table

6.3.1 Writing a thread-safe lookup table using locks

This section designs a thread-safe lookup table whose revised interface and per-bucket locks allow more concurrent access than a single lock around a standard map.

Key terms: iterators, add_or_update_mapping, value_for, default_value, hash table, buckets, hash function, fine-grained locking, std::hash<>, get_bucket()

6.3.2 Writing a thread-safe list using locks

This section builds a thread-safe linked list with per-node locks, explains its operations, and identifies the safety rules and concurrency limits of the design.

Key terms: STL-style iterator, for_each, user-supplied code, deadlock, data races, threadsafe_list<>, node, head, push_front(), mutex

6.4 Summary

The summary recaps how the chapter applies concurrency-design guidelines to lock-based data structures and points ahead to lock-free designs using the same guidelines.

Where this is used later

Concepts explained in this chapter

← Chapter 5: Chapter 5. The C++ memory model and operations on atomic types Chapter 7: Chapter 7. Designing lock-free concurrent data structures →

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.