In this lesson, you will learn how to give every thread its own private "sandbox" for data using Thread-Local Storage (TLS).
The Thread Sharing Problem
Normally, when you declare a static field, every thread in your program sees and modifies the exact same memory location. This is often dangerous; if two threads try to update the same counter at the same time, you get a "race condition" where updates are lost or data becomes corrupted.
Sometimes, you don't actually want threads to share data. You might want each thread to keep track of its own independent state—like a unique ID, a logger, or a calculation buffer—without having to worry about locking mechanisms.
Enter Thread-Local Storage
Thread-Local Storage allows you to declare a variable that is static, but instead of being shared, the runtime creates a separate instance for every thread that accesses it.
The easiest way to implement this in C# is by using the [ThreadStatic] attribute on a static field.
public class Worker
{
// Each thread gets its own independent copy of 'counter'
[ThreadStatic]
public static int counter;
public void DoWork()
{
counter++;
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} count: {counter}");
}
}
How it Works
When Thread A accesses counter, the runtime points it to a memory slot specifically for Thread A. When Thread B accesses counter, it receives a different memory slot.
- Isolation: You don't need locks (
lockorMonitor) because no other thread can touch your specific instance. - Persistence: The value stays alive as long as that specific thread is alive.
- Caveat: Because this relies on the thread identity, it behaves differently if you move tasks between threads (for instance, using
async/awaitwhere a method might resume on a different thread). Always initialize these values carefully, as they default to the type's default value (e.g.,0forint) for every new thread.
Key Takeaway: The [ThreadStatic] attribute creates a unique, isolated copy of a static variable for every thread, allowing you to maintain thread-specific state without the overhead or complexity of synchronization locks.