I know that I can create an immutable (i.e. thread-safe) object like this:
class CantChangeThis
{
private readonly int value;
public CantChangeThis(int value)
{
this.value = value;
}
public int Value { get { return this.value; } }
}
However, I typically "cheat" and do this:
class CantChangeThis
{
public CantChangeThis(int value)
{
this.Value = value;
}
public int Value { get; private set; }
}
Then I got wondering, "why does this work?" Is it really thread-safe? If I use it like this:
var instance = new CantChangeThis(5);
ThreadPool.QueueUserWorkItem(() => doStuff(instance));
Then what it's really doing is (I think):
- Allocating space on the thread-shared heap for the instance
- Initializing the value inside the instance on the heap
- Writing a pointer/reference to that space into the local variable (thread-specific stack)
- Passing the reference to that thread as a value. (Interestingly the way I've written it, the reference is inside a closure, which is doing the same thing that my instance is doing, but let's ignore that.)
- Thread goes to the heap and reads data from the instance.
However, that instance value is stored in shared memory. The two threads might have cache-inconsistent views of that memory on the heap. What is it that makes sure the threadpool thread actually sees the constructed instance and not some garbage data? Is there an implicit memory barrier at the end of any object construction?