107

[ThreadStatic] is defined using attribute while ThreadLocal<T> uses generic. Why different design solutions were chosen? What are the advantages and disadvantages of using generic over attributes in this case?

user2341923
  • 4,537
  • 6
  • 30
  • 44
  • 4
    See http://reedcopsey.com/2009/11/12/thread-specific-data-becomes-easier-in-net-4-0-via-threadlocalt/ - I don't see what this has to do with reflection though... – Jon Skeet Aug 20 '13 at 11:31

3 Answers3

128

Something the blog post noted in the comments doesn't make explicit, but I find to be very important, is that [ThreadStatic] doesn't automatically initialize things for every thread. For example, say you have this:

[ThreadStatic]
private static int Foo = 42;

The first thread that uses this will see Foo initialized to 42. But subsequent threads will not. The initializer works for the first thread only. So you end up having to write code to check if it's initialized.

ThreadLocal<T> solves that problem by letting you supply an initialization function (as Reed's blog shows) that's run before the first time the item is accessed.

In my opinion, there is no advantage to using [ThreadStatic] instead of ThreadLocal<T>.

Alexander Derck
  • 13,818
  • 5
  • 54
  • 76
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • 22
    Except perhaps that [`ThreadLocal`](http://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx) is available in .NET 4 and up, and [the `ThreadStatic` attribute](http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(v=vs.100).aspx) is also available in 3.5 and below. – Jeroen Aug 25 '14 at 11:06
  • 4
    And if you're not using initializers to set the value, but are instead setting it at some later point after initialization, using [ThreadStatic] is syntactically cleaner. – Thought Apr 09 '15 at 19:42
  • `[ThreadStatic]` has the advantage of not having to change existing references to that field in code. – NextInLine May 20 '15 at 17:08
  • 9
    And except that `ThreadLocal` implements `IDisposable` and usually forces you to implement `IDisposable` as well, which forces your callers to dispose you and therefore implement `IDisposable` as well ... – Stefan Steinegger Jul 24 '15 at 10:36
  • By the way: is the `ThreadLocal` initialized when the thread comes from the thread pool? Otherwise it wouldn't be very useful, because it would take data from another thread over. (`[ThreadStatic]` is not initialized when the thread is coming from the pool, but there the expectations are low ...) – Stefan Steinegger Jul 24 '15 at 10:43
  • 4
    @StefanSteinegger: I would be very careful using `ThreadLocal` or `ThreadStatic` with pool threads. Those values will remain through the entire life of the pool thread, not just for the task that you assign it. That can cause you trouble in some pretty non-obvious ways. See http://stackoverflow.com/questions/561518/is-thread-local-storage-persisted-between-backgroundworker-invocations and similar questions for more info. – Jim Mischel Jul 25 '15 at 18:02
  • 3
    Shouldn't the field in the example also be declared `static`? See https://msdn.microsoft.com/en-us/library/system.threadstaticattribute(v=vs.110).aspx – entheh Apr 21 '16 at 08:15
  • 2
    ThreadLocal<> seems to be slightly slower compared to ThreadStatic. – Mehran Apr 28 '17 at 16:30
  • @Mehran: Slower in what way? Are you saying that access to a variable defined with `ThreadLocal<>` is slower than access to a variable defined with `ThreadStatic`? That seems pretty odd. Do you have a program I can run to test and verify this? – Jim Mischel Apr 28 '17 at 16:35
  • 2
    If you look at the implementation of ThreadLocal (https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Threading/ThreadLocal.cs) you see it's using ThreadStatic behind the scene. This is a naive benchmark https://pastebin.com/Ym55kZi9 – Mehran Apr 28 '17 at 17:47
  • @Mehran: So I guess the moral of the story is that if you're worried about a few microseconds here and there, you probably should use an instance variable or `ThreadStatic`. – Jim Mischel Apr 28 '17 at 17:55
43

ThreadStatic Initialize only on first thread, ThreadLocal Initialize for each thread. Below is the simple demonstration:

    public static ThreadLocal<int> _threadlocal =
        new ThreadLocal<int>(() =>
        {
            return Thread.CurrentThread.ManagedThreadId;
        });

    public static void Main()
    {
        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("First Thread: {0}", x);
            }
        }).Start();

        new Thread(() =>
        {
            for (int x = 0; x < _threadlocal.Value; x++)
            {
                Console.WriteLine("Second Thread: {0}", x);
            }
        }).Start();

        Console.ReadKey();
    }

enter image description here

marai
  • 865
  • 1
  • 11
  • 16
18

The main idea behind ThreadStatic is to maintain a separate copy of the variable for each thread.

class Program
    {
        [ThreadStatic]
        static int value = 10;

        static void Main(string[] args)
        {
            value = 25;

            Task t1 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T1: " + value);
            });
            Task t2 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T2: " + value);
            });
            Task t3 = Task.Run(() =>
            {
                value++;
                Console.WriteLine("T3: " + value);
            });

            Console.WriteLine("Main Thread : " + value);

            Task.WaitAll(t1, t2, t3);
            Console.ReadKey();
        }
    }

In the above snippet, we have a separate copy of value for each thread, including the main thread.

enter image description here

So, a ThreadStatic variable will be initialized to its default value on other threads except the thread on which it is created.

If we want to initialize the variable on each thread in our own way, use ThreadLocal.

CSR
  • 770
  • 1
  • 14
  • 30