Table of contents : Inside front cover Learn Concurrent Programming with Go Copyright contents front matter preface acknowledgments about this book Who should read this book How this book is organized: A road map How to read the book About the code liveBook discussion forum about the author about the cover illustration Part 1. Foundations 1 Stepping into concurrent programming 1.1 About concurrency 1.2 Interacting with a concurrent world 1.3 Increasing throughput 1.4 Improving responsiveness 1.5 Programming concurrency in Go 1.5.1 Goroutines at a glance 1.5.2 Modeling concurrency with CSP and primitives 1.5.3 Building our own concurrency tools 1.6 Scaling performance 1.6.1 Amdahl’s law 1.6.2 Gustafson’s law Summary 2 Dealing with threads 2.1 Multiprocessing in operating systems 2.2 Abstracting concurrency with processes and threads 2.2.1 Concurrency with processes 2.2.2 Creating processes 2.2.3 Using multiprocessing for common tasks 2.2.4 Concurrency with threads 2.2.5 A multithreaded application in practice 2.2.6 Using multiple processes and threads together 2.3 What’s so special about goroutines? 2.3.1 Creating goroutines 2.3.2 Implementing goroutines in the user space 2.3.3 Scheduling goroutines 2.4 Concurrency versus parallelism 2.5 Exercises Summary 3 Thread communication using memory sharing 3.1 Sharing memory 3.2 Memory sharing in practice 3.2.1 Sharing a variable between goroutines 3.2.2 Escape analysis 3.2.3 Updating shared variables from multiple goroutines 3.3 Race conditions 3.3.1 Stingy and Spendy: Creating a race condition 3.3.2 Yielding execution does not help with race conditions 3.3.3 Proper synchronization and communication eliminate race conditions 3.3.4 The Go race detector 3.4 Exercises Summary 4 Synchronization with mutexes 4.1 Protecting critical sections with mutexes 4.1.1 How do we use mutexes? 4.1.2 Mutexes and sequential processing 4.1.3 Non-blocking mutex locks 4.2 Improving performance with readers–writer mutexes 4.2.1 Go’s readers–writer mutex 4.2.2 Building our own read-preferred readers–writer mutex 4.3 Exercises Summary 5 Condition variables and semaphores 5.1 Condition variables 5.1.1 Combining mutexes with condition variables 5.1.2 Missing the signal 5.1.3 Synchronizing multiple goroutines with waits and broadcasts 5.1.4 Revisiting readers–writer locks using condition variables 5.2 Counting semaphores 5.2.1 What’s a semaphore? 5.2.2 Building a semaphore 5.2.3 Never miss a signal with semaphores 5.3 Exercises Summary 6 Synchronizing with waitgroups and barriers 6.1 Waitgroups in Go 6.1.1 Waiting for tasks to complete with waitgroups 6.1.2 Creating a waitgroup type using semaphores 6.1.3 Changing the size of our waitgroup while waiting 6.1.4 Building a more flexible waitgroup 6.2 Barriers 6.2.1 What is a barrier? 6.2.2 Implementing a barrier in Go 6.2.3 Concurrent matrix multiplication using barriers 6.3 Exercises Summary Part 2. Message passing 7 Communication using message passing 7.1 Passing messages 7.1.1 Passing messages with channels 7.1.2 Buffering messages with channels 7.1.3 Assigning a direction to channels 7.1.4 Closing channels 7.1.5 Receiving function results with channels 7.2 Implementing channels 7.2.1 Creating a channel with semaphores 7.2.2 Implementing the Send() function in our channel 7.2.3 Implementing the Receive() function in our channel 7.3 Exercises Summary 8 Selecting channels 8.1 Combining multiple channels 8.1.1 Reading from multiple channels 8.1.2 Using select for non-blocking channel operations 8.1.3 Performing concurrent computations on the default case 8.1.4 Timing out on channels 8.1.5 Writing to channels with select 8.1.6 Disabling select cases with nil channels 8.2 Choosing between message passing and memory sharing 8.2.1 Balancing code simplicity 8.2.2 Designing tightly versus loosely coupled systems 8.2.3 Optimizing memory consumption 8.2.4 Communicating efficiently 8.3 Exercises Summary 9 Programming with channels 9.1 Communicating sequential processes 9.1.1 Avoiding interference with immutability 9.1.2 Concurrent programming with CSP 9.2 Reusing common patterns with channels 9.2.1 Quitting channels 9.2.2 Pipelining with channels and goroutines 9.2.3 Fanning in and out 9.2.4 Flushing results on close 9.2.5 Broadcasting to multiple goroutines 9.2.6 Closing channels after a condition 9.2.7 Adopting channels as first-class objects 9.3 Exercises Summary Part 3. More concurrency 10 Concurrency patterns 10.1 Decomposing programs 10.1.1 Task decomposition 10.1.2 Data decomposition 10.1.3 Thinking about granularity 10.2 Concurrency implementation patterns 10.2.1 Loop-level parallelism 10.2.2 The fork/join pattern 10.2.3 Using worker pools 10.2.4 Pipelining 10.2.5 Pipelining properties 10.3 Exercises Summary 11 Avoiding deadlocks 11.1 Identifying deadlocks 11.1.1 Picturing deadlocks with resource allocation graphs 11.1.2 Deadlocking in a ledger 11.2 Dealing with deadlocks 11.2.1 Detecting deadlocks 11.2.2 Avoiding deadlocks 11.2.3 Preventing deadlocks 11.3 Deadlocking with channels 11.4 Exercises Summary 12 Atomics, spin locks, and futexes 12.1 Lock-free synchronization with atomic variables 12.1.1 Sharing variables with atomic numbers 12.1.2 Performance penalty when using atomics 12.1.3 Counting using atomic numbers 12.2 Implementing a mutex with spin locks 12.2.1 Comparing and swapping 12.2.2 Building a mutex 12.3 Improving on spin locking 12.3.1 Locking with futexes 12.3.2 Reducing system calls 12.3.3 Go’s mutex implementation 12.4 Exercises Summary index Inside back cover