Reputation: 3355
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
Reputation: 3355
[ThreadStatic]
static Object LocalObject;
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
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.
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