6

Everyone says static initializers are thread-safe, but I'm worried about a particular detail.

Let's say I have

static class MyStaticClass
{
    public static readonly object myField = MyOtherClass.GetNewObject();
}

static class MyOtherClass
{
    public static object GetNewObject()
    { /* arbitrary code that returns a new object */ }
}

Which of the following does C# guarantee, when MyStaticClass.myField is not yet initialized?

  1. If threads 1 and 2 try to access myField together (in that order), GetNewObject will have started executing before thread 2 reads myField.

  2. If threads 1 and 2 try to access myField together (in that order), GetNewObject will have finished executing before thread 2 reads myField.

How about the CLR in general: if its guarantees differ from C#'s, in what ways do they differ?
Has the behavior changed in more recent versions of the .NET framework?

Note:

It's a tricky question, and I think a complete answer would probably mention the difference between a static constructor and a static initializer, and how they interact with beforefieldinit to produce the claimed result.

Community
  • 1
  • 1
user541686
  • 189,354
  • 112
  • 476
  • 821
  • I don't understand the relevance of `GetNewObject()`? Is it the method that accesses the field in the other class ? – Simon Belanger Jun 26 '13 at 21:28
  • @SimonBelanger: There's nothing significant about it, I just put it in a separate class to show that it might be arbitrary code. If the answer changes depending on where/how `GetNewObject` is defined then I'd like to know that too. – user541686 Jun 26 '13 at 21:29
  • 1
    I don't say that static initializers are thread safe. I've said the opposite many times. Programs which deadlock due to mistakes in static initializers are not thread safe. Therefore your opening sentence is false. – Eric Lippert Jun 27 '13 at 14:16

2 Answers2

3

Case 2 will be honoured. A class field, property, or method cannot be dereferenced until the type has been initialized, and the type will not be initialized until the static constructor is completed. The static constructor is, to the best of my knowledge, therefore a blocking call.

http://msdn.microsoft.com/en-us/library/aa645612(v=vs.71).aspx

"The static constructor for a class executes at most once in a given application domain."

See this reply from Eric Lippert: https://stackoverflow.com/a/9399027/2420979 and note that "cctor" is IL for static constructor.

No cctors call MyMethod, directly or indirectly! Now is it ever possible for a static method like MyMethod to be called before the cctor of MyClass completes?

No.

Is that still true even if there are multiple threads involved?

Yes. The cctor will finish on one thread before the static method can be called on any thread.

Can the cctor be called more than once? Suppose two threads both cause the cctor to be run.

The cctor is guaranteed to be called at most once, no matter how many threads are involved. If two threads call MyMethod "at the same time" then they race. One of them loses the race and blocks until the MyClass cctor completes on the winning thread.

Community
  • 1
  • 1
Haney
  • 28,087
  • 7
  • 52
  • 64
  • The fact that it executes once doesn't imply it has finished executing before the second access. – user541686 Jun 26 '13 at 21:32
  • Access cannot happen until the type is initialized, and the type initializer/constructor will only execute ONCE, therefore logically the access must wait for the type to initialize without repeating the constructor or initializer logic. – Haney Jun 26 '13 at 21:35
  • I don't follow. It could very well be that the initializer will execute *once* on the first access, but if the field hasn't been yet assigned to, the second thread just reads `null` instead of waiting for it to be initialized. You may be correct, but your reason doesn't imply the conclusion you mentioned. – user541686 Jun 26 '13 at 21:35
  • What I'm saying is that the static constructor/initializer mechanism is a blocking call, which will fire upon first access (lazy) and will block all access from any thread until completion. – Haney Jun 26 '13 at 21:36
  • I understand what you're saying, but my question is, how do you know it's true? The sentence you quoted doesn't imply it to be true, because there are other ways to satisfy the quote (e.g. reading null on the second thread, as I mentioned above). – user541686 Jun 26 '13 at 21:37
  • 1
    Ahh... the *"Is that still true even if there are multiple threads involved?"* answers my question, thanks! – user541686 Jun 26 '13 at 21:45
  • 1
    Took a bit of digging to confirm my understanding as well. Thanks for the exercise. :) – Haney Jun 26 '13 at 21:46
2

Taken from MSDN:

Static members are initialized before the static member is accessed for the first time and before the static constructor, if there is one, is called.

If you second method is run in two different threads but never use the static class, it will never be built. However, if there is a reference to it, it will be initialized before any of the two thread access it.

Simon Belanger
  • 14,012
  • 3
  • 36
  • 34