← Concepts

阅读中文版本

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.

concurrency

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

https://en.wikipedia.org/wiki/Thread-local_storage

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.