Concurrency Patterns

Concurrency lets a program make progress on multiple tasks without waiting on each one sequentially — essential for responsive UIs, high-throughput servers, and using multi-core hardware. But it's also where some of the hardest bugs live: race conditions and deadlocks that appear only under specific timing.

The first key distinction: concurrency is about structure (dealing with many things at once), while parallelism is about execution (doing many things at once). Async I/O gives you concurrency on a single core; parallelism needs multiple cores.

TL;DR

Quick Example

Go's CSP model — communicate over a channel instead of sharing memory:

Core Concepts

Concurrency vs parallelism

Models

Common problems

Patterns by Language

Python — threads for I/O, processes for CPU

Go — goroutines, channels, worker pools

JavaScript — concurrent awaits, Web Workers for CPU

Best Practices

Common Mistakes

Unguarded shared state

Using threads for CPU-bound Python work

FAQ

What's the difference between concurrency and parallelism?

Concurrency is structuring a program to handle multiple tasks that can make progress independently (interleaving); parallelism is literally running them at the same time on multiple cores. You can have concurrency on a single core (async I/O); parallelism requires multiple cores.

Async or threads or processes?

Use async (event loop) for I/O-bound work — many waiting tasks on one thread. Use threads for I/O concurrency in languages without the GIL constraint, and processes for CPU-bound work (especially in Python, to bypass the GIL). Match the tool to whether you're waiting on I/O or burning CPU.

How do I avoid race conditions and deadlocks?

Minimize shared mutable state; prefer message passing (channels) or immutable data. When you must share, protect it with locks acquired in a consistent order (to avoid deadlock), keep critical sections short, and consider higher-level concurrency primitives that encapsulate the synchronization.

Why is Go's concurrency model praised?

Goroutines are cheap (you can run thousands), and channels encourage "share memory by communicating" rather than locking shared state — which sidesteps a whole class of bugs. Combined with context for cancellation, it makes concurrent code approachable. See Go.

Related Topics

References