Search code examples
c#multithreadingvolatile

C# not thread-safe initialization


There are two threads A and B. Thread A performs the following assignment:

v = new Vec2()

Thread B uses v, for example:

double length = Math.Sqrt(v.x*v.x + v.y*v.y);
Console.WriteLine("length is {0}", length);

v is a non-volatile field, Vec2 is a reference type with some initialization:

class Vec2
{
   public double x;
   public double y;
   public Vec2()
   {
       // this one is important
       x = 56.0;
       y = 78.0
   }
}

The question is, according to C# language spec (or/and .NET runtime spec), might it be that the thread B will observe v uninitialized? That is, might it be that the reference assignment to v will be observed by thread B before e.g. x = 56.0?

EDIT: if so, would adding volatile ensure that thread B observes v only after it is fully initialized?

EDIT: will the answer change if x,y are initialized using field initializers, that is:

class Vec2
{
    public double x = 56.0;
    public double y = 78.0;
}

EDIT: I do not seek to solve any problem here, just want to know what will actually happen. Thanks for your answers!


Solution

  • The question is, according to C# language spec (or/and .NET runtime spec), might it be that the thread B will observe v uninitialized?

    Thread B will not be able to access the field within v prior to the constructor running, but it may "see" v as null, depending on when the initialization in thread A occurs and whether or not there is a memory barrier in place between the initialization and the access by Thread B.

    In general, if you have two threads, and you require something to happen in order (in this case, v to be initialized by Thread A prior to being used in Thread B), you will need to explicitly add some form of synchronization.

    In your case, something as simple as a wait handle would suffice:

    // Assuming the existence of this in scope of both threads:
    // ManualResetEvent mre = new ManualResetEvent(false)
    

    In thread A:

    v = new Vec2();
    mre.Set(); // Denote that initialization is complete
    

    In thread B:

    mre.WaitOne(); // Wait (blocking) until initialization is complete
    double length = Math.Sqrt(v.x*v.x + v.y*v.y);
    // .. Other code as needed
    

    EDIT: if so, would adding volatile ensure that thread B observes v only after it is fully initialized?

    Volatile is not going to be the correct solution. If you need to wait for initialization to occur, you should use a synchronization mechanism, such as the one detailed above.

    EDIT: will the answer change if x,y are initialized using field initializers, that is:

    No. Both the constructor and field initializers will all run prior to v being "set", but you can't guarantee that v will not be null without some other synchronization in place.