Jay
Jay

Reputation: 3355

.Net Core 3.1.6 ThreadLocal / ThreadStatic

Quick question regarding ThreadStatic and ThreadLocal in C#, specifically .Net Core 3.1.6..

I would rather not post the exact example but it's very similar to this:

[ThreadStatic]
static readonly Object LocalObject = new Object();

I then access said object from multiple different threads using ParrallelEnumerable or Tasks.Parallel and I run into a very interesting exception which unfortunately crashed the runtime...

The intention of my code is that every Thread which accesses LocalObject will have it's own instance as shown

Are there any known issues around ThreadLocal / ThreadStatic in .Net Core 3.1.6 and where can I read about them?

If there is nothing indicated as changed or different for 3.1.6 are there changes in 5.0 related to the same attributes? If none of those then has .Net core changed the behavior of these constructs from their Full Framework implementation?

Thank you for your time!

Upvotes: -2

Views: 1790

Answers (2)

Jay
Jay

Reputation: 3355

[ThreadStatic]
static Object LocalObject;

And

Do not specify initial values for fields marked with ThreadStaticAttribute, because such initialization occurs only once, when the class constructor executes, and therefore affects only one thread. If you do not specify an initial value, you can rely on the field being initialized to its default value if it is a value type, or to null if it is a reference type.

So we remove the readonly and check for null on each access and store to the static on each thread. (The static constructor still only runs once)

See also AsyncLocal<T> especially if using ThreadPool threads, And Disposing thread static variable

Upvotes: 2

Luis
Luis

Reputation: 874

Based on the documentation you provided in the link, the problems is that when you access the variable, the value is null because:

One thing to watch out for is that if we initialize the ThreadStatic variable, for example if we write the following

[ThreadStatic]
static int value = 10;

you need to be aware that this is initialized only on the thread it’s declared on, all the threads which use value will get a variable initialised with it’s default value, i.e. 0.

So, if you try to do something with the variable, expecting the variable to have an Object instance, it will throw an exception which if not handled will make the application fail.

Edit - 7/21/2020

After some tests, I finally came to a conclusion: The field cannot be readonly due to initialization issues, that is, the field would be initialized only for the first thread, inline or in the static constructor. Any subsequent thread will only get a null value.

The closest I was able to get is as follows:

public static void Main(string[] args)
{

    var tsk1 = Task.Run(() => {
       new Test().Display();
    });
    var tsk2 = Task.Run(() => {
       new Test().Display();
    });
    Task.WaitAll(new Task[] { tsk1, tsk2 });
}

class Test {
   [ThreadStatic]
   static Object _localObject;

   // Creates a new instance per thread.
   static Object LocalObject => _localObject ?? (_localObject = new Object());

   public void Display() {
      Console.WriteLine(LocalObject.GetHashCode());
   } 
}

// Sample output:
// C:\Test> dotnet run
// 4032828
// 6044116

Upvotes: 1

Related Questions