Andrew Hill
Andrew Hill

Reputation: 2020

thread safe construction with attribute assignment

from https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers

class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }
}

Cat cat = new Cat { Age = 10, Name = "Fluffy" };

The object initializers syntax allows you to create an instance, and after that it assigns the newly created object, with its assigned properties, to the variable in the assignment.

vs https://stackoverflow.com/a/19138412/432976

var albumData = new Album 
{
     Name = "Albumius",
     Artist = "Artistus",
     Year = 2013
 };

is syntactic shorthand for this equivalent code:

var albumData = new Album();
albumData.Name = "Albumius";
albumData.Artist = "Artistus";
albumData.Year = 2013;

The two are identical after compilation.

Question: is this stype of construction + assignment thread safe? (ie, could another thread reading cat see the Cat between when it was created, and when Age and Name are assigned)?

The first seems to say it is, as it is after the properties are assigned, the variable gets assigned (the threadsafe order), the second says that at a compiled code level, the order is different.

if the second is true, is the following code sufficient to avoid the race condition of another thread seeing a half constructed cat?

var myNewCat = new Cat { Age = 10, Name = "Fluffy" };
sharedCat = myNewCat;

I realise that there are secondary race conditions here concerning if other threads see the oldCat or the newCat, but in this situation my only concern is that other threads must see a complete Cat, not a half constructed one.

Upvotes: 2

Views: 786

Answers (3)

angelsix
angelsix

Reputation: 392

Using the following example class

public class Cat
{
    public int Age { get; set; }
    public string Name { get; set; }
}

C# 2.0 Style

Code

var cat1 = new Cat();
cat1.Age = 10;
cat1.Name = "Fluffy";

Generates the following IL code (inspected using .Net Reflector)

L_000c: newobj instance void ConsoleApp1.Cat::.ctor()
L_0011: stloc.0 
L_0012: ldloc.0 
L_0013: ldc.i4.s 10
L_0015: callvirt instance void ConsoleApp1.Cat::set_Age(int32)
L_001a: nop 
L_001b: ldloc.0 
L_001c: ldstr "Fluffy"
L_0021: callvirt instance void ConsoleApp1.Cat::set_Name(string)
L_0026: nop 

This basically creates the variable instance and its available (from stloc.0), so at that point it is available for another thread to pick it up in that state if it was exposed.

Answer is no this version is not thread safe

C# 3.0 Style

Since C# 3 onwards we can do what is called Object Initializers.

Code

var cat1 = new Cat { Age = 10, Name = "Fluffy" };

Generates the following IL code

L_000c: newobj instance void ConsoleApp1.Cat::.ctor()
L_0011: dup 
L_0012: ldc.i4.s 10
L_0014: callvirt instance void ConsoleApp1.Cat::set_Age(int32)
L_0019: nop 
L_001a: dup 
L_001b: ldstr "Fluffy"
L_0020: callvirt instance void ConsoleApp1.Cat::set_Name(string)
L_0025: nop 
L_0026: stloc.0 

The main difference here is the instance of the class is never pulled off the evaluation stack as it uses dup until its all done then does stloc.0 last.

Answer is this method is thread-safe

Upvotes: 2

Artak
Artak

Reputation: 2887

A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time. In the example you have provided as is, there is no shared state, as the instance is assigned to a local variable. Hence, either way (and yes, they're the same) is always thread-safe:

var albumData = new Album 
{
     Name = "Albumius",
     Artist = "Artistus",
     Year = 2013
 };

The situation changes, as soon as the variable being assigned is not local to the method it's in (a field of a class for example):

class TestClass
{
  private Album album;

  public void TestAssignment(string name)
  {
    // Here I'm using this style of property assignment to make it very explicit why it's not thread-safe
    this.album = new Album();
    this.album.Name = name;
    ...
  }
}

As you can see, the TestAssignment() method now can be called from different threads at the same time. Depending on which thread will execute each line, different values will be assigned to the album instance as well as it's name. In this case, it would make sense to think about thread-safety mechanisms.

Hope this helps to clarify the difference.

Upvotes: -1

camios
camios

Reputation: 342

@JonSkeet says the equivalent code introduces a temp variable.

var tmp = new Album();
tmp.Name = "Albumius";
tmp.Artist = "Artistus";
tmp.Year = 2013;
var albumData = tmp;

Or for the Cat

var tmp = new Cat();
tmp.Age = 10;
tmp.Name = "Fluffy";
Cat cat = tmp;

So if the reference assignment is threadsafe, then the object initialiser would be threadsafe. Right?

Upvotes: 2

Related Questions