-2

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!

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Jay
  • 3,276
  • 1
  • 28
  • 38
  • 3
    the error is very likely with your code and not the runtime – Garr Godfrey Jul 21 '20 at 04:27
  • 5
    @Jay `[ThreadStatic]` should not be initialized like this, because this means, that only for current thread it is initialized, it will not be initialized for every thread, so in different thread, you must check if its null and initialize it again. Somewhere implementer of that library has forgotten to initialize it correctly. – Akash Kava Jul 21 '20 at 04:38
  • Using ThreadLocal completely eliminates the ThreadStatic assignment issues. Is “the problem” _really_ the same for both approaches? Revise the title accordingly. I also recommend removing ‘begging the bug’ and use a more relevant summary about the observed behavior instead. – user2864740 Jul 22 '20 at 05:08

2 Answers2

2
[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

Jay
  • 3,276
  • 1
  • 28
  • 38
  • Still begs for the question of how you achieve this with `static readonly`... – Jay Jul 21 '20 at 23:24
  • 1
    No. It generally doesn’t make sense for a readonly ThreadStatic. This answer and the quoted excerpt explains why. I suppose, there might be a fringe case to identity the thread the class initialization occurred on.. [ThreadStatic] private readonly initThread = true.. although I’ve never seen that in actual code. – user2864740 Jul 22 '20 at 05:14
1

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
Luis
  • 866
  • 4
  • 7
  • https://stackoverflow.com/questions/5227676/how-does-the-threadstatic-attribute-work This post indicates that each thread will have it's own copy ... Although its not for .net core... Can you explain further? – Jay Jul 21 '20 at 12:10
  • See also https://stackoverflow.com/questions/868537/threadstatic-modified-with-static-c-sharp – Jay Jul 21 '20 at 12:11
  • Are you saying I need a Local in addition to the static and for each local to be instantiated as required when the static is null? I can try that... Can you tell me if this is behavior change from the full fx? Thanks – Jay Jul 21 '20 at 12:12
  • 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. – Jay Jul 21 '20 at 12:16
  • Thanks for your answer, a demonstration on how this would work with a `static readonly` would be helpful. – Jay Jul 22 '20 at 01:14
  • 2
    I updated my answer. Sadly, I found no way to make it work with field `static readonly`, as I explained in my update. – Luis Jul 22 '20 at 02:59
  • 1
    (It never makes sense for a readonly ThreadStatic, in _any_ C# version or .NET implementation. One can get readonly with ThreadLocals, or even direct TLS, or one of the newer Async-aware forms.) – user2864740 Jul 22 '20 at 05:12