Thread-Local Storage
Thread-local storage gives each thread its own copy of a variable, avoiding sharing but breaking under pools and async scheduling where threads are reused.
Definition
Thread-local storage is a mechanism that allocates a distinct instance of a variable per thread, so each thread reads and writes its own copy without synchronisation.
Context and scope
It is used for per-request context, buffers, and statistics, but requires care when work migrates between threads.
Common misconceptions
- thread-local values are cleaned up automatically when a request ends
- thread-local storage is a substitute for passing context explicitly
Anti-patterns
- storing request identity in thread-local state behind a thread pool
- assuming async continuations resume on the same thread
Related concepts
Related examples
Authoritative source
Definition
Thread-local storage is a mechanism that allocates a distinct instance of a variable per thread, so each thread reads and writes its own copy without synchronisation.
It is used for per-request context, buffers, and statistics, but requires care when work migrates between threads. Compared to simpler alternatives, this concept fits best when the trade-off it represents is the one the system actually cares about.
When to use it
A common use case is applying it at the boundary where the cost of getting it wrong is highest, and the scenario usually involves a concrete guarantee the system must uphold. Unlike an ad hoc approach, it makes that guarantee explicit, reviewable, and testable.
How it works
Because a pooled thread is reused for many requests, a stale value left in thread-local storage can leak from one request to the next, a subtle data isolation bug.
In async and coroutine runtimes the logical task may resume on a different thread, so context must follow the task rather than the thread.
Trade-offs and alternatives
Prefer explicit propagation of context, or a runtime-provided context object, and if thread-local storage is used, always reset it in a finally block. The main drawback is the extra complexity it introduces, so the benefit should clearly outweigh that cost before a team adopts it.
Common misconceptions
A persistent misconception is that thread-local values are cleaned up automatically when a request ends. Another is that thread-local storage is a substitute for passing context explicitly. For example, an engineer who assumes otherwise may design around a guarantee that does not actually exist, which only becomes obvious under load or failure.
Anti-patterns to avoid
Two recurring anti-patterns are to storing request identity in thread-local state behind a thread pool and to assuming async continuations resume on the same thread. Both tend to surface as hard-to-debug production issues rather than obvious test failures. Related concepts include Asynchronous I/O, Goroutine and Task Leaks, I/O Multiplexing with epoll and kqueue.