Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the ThreadStatic attribute work?

People also ask

How does ThreadStatic work?

Remarks. A static field marked with ThreadStaticAttribute is not shared between threads. Each executing thread has a separate instance of the field, and independently sets and gets values for that field. If the field is accessed on a different thread, it will contain a different value.

How do I make a static variable thread safe in C#?

public static readonly object CounterLock = new object(); ... lock ( CounterLock ) { Counter++; } ... The point is that all reads / writes must be protected by the lock - it's not enough to protect a single place because then threads doing reads or writes may still make a change when a lock elsewhere is in effect.

Why do we use static variables in C#?

Static variables are used for defining constants because their values can be retrieved by invoking the class without creating an instance of it. Static variables can be initialized outside the member function or class definition.

What is Threadlocal C#?

Thread Local Storage is used to store thread-specific pieces of data. Thread-local storage (TLS) is a computer programming method that uses static or global memory local to a thread. All threads of a process share the virtual address space of the process.


How does [ThreadStatic] attribute work?

You can think that the field marked with ThreadStatic is attached to a thread and its lifetime is comparable to the lifetime of a thread.

So in pseudocode ThreadStatic is similar (by semantics) to having a key-value attached to a thread:

Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myVariable"] += 1;

but the syntax is just a bit easier:

class MyClass {
  [ThreadStatic]
  static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;

what happens if you put it on a non-static member?

I believe it is ignored:

    class A {
        [ThreadStatic]
        public int a;
    }
    [Test]
    public void Try() {
        var a1 = new A();
        var a2 = new A();
        a1.a = 5;
        a2.a = 10;
        a1.a.Should().Be.EqualTo(5);
        a2.a.Should().Be.EqualTo(10);
    }

Additionally it is worth mentioning that ThreadStatic does not require any synchronisation mechanism as compared to normal static fields (because the state is not shared).


The implementation semantics of thread static are below the IL level, in the .NET jit compiler. Compilers that emit to IL like VB.NET and C# don't need to know anything about Win32 TLS in order to emit IL code that can read and write a variable that has the ThreadStatic attribute. There's nothing special about the variable as far as C# knows - it's just a location to read and write stuff. The fact that it has an attribute on it is of no consequence to C#. C# only needs to know to emit IL read or write instructions for that symbol name.

The 'heavy lifting' is done by the core CLR that is responsible for making the IL work on a particular hardware architecture.

That would also explain why putting the attribute on an inappropriate (non-static) symbol doesn't get a reaction from the compiler. The compiler doesn't know what special semantics the attribute requires. Code analysis tools like FX/Cop, though, should know about it.

Another way to look at it: CIL defines a set of storage scopes: static (global) storage, member storage, and stack storage. TLS isn't on that list, very likely because TLS doesn't need to be on that list. If IL read and write instructions are sufficient to access TLS when the symbol is tagged with a TLS attribute, why should IL have any special representation or treatment for TLS? It's not needed.


The [ThreadStatic] creates isolated versions of the same variable in each thread.

Example:

[ThreadStatic] public static int i; // Declaration of the variable i with ThreadStatic Attribute.

public static void Main()
{
    new Thread(() =>
    {
        for (int x = 0; x < 10; x++)
        {
            i++;
            Console.WriteLine("Thread A: {0}", i); // Uses one instance of the i variable.
        }
    }).Start();

    new Thread(() =>
   {
       for (int x = 0; x < 10; x++)
       {
           i++;
           Console.WriteLine("Thread B: {0}", i); // Uses another instance of the i variable.
       }
   }).Start();
}

The field marked with [ThreadStatic] are created on Thread Local Storage so every thread has it own copy of the field i.e the scope of the fields are local to the thread.

TLS fields are access through gs/fs segments registers.These segments are used by the OS kernels to access thread-specific memory.The .net compiler do not emit any IL to stuff/retrieve the value in the TLS. It is done by the OS kernel.