When implementing a class intended to be thread-safe, should I include a memory barrier at the end of its constructor, in order to ensure that any internal structures have c
No, you don't need memory barrier in the constructor. Your assumption, even though demonstrating some creative thought - is wrong. No thread can get a half backed instance of queue
. The new reference is "visible" to the other threads only when the initialization is done. Suppose thread_1 is the first thread to initialize queue
- it goes through the ctor code, but queue
's reference in the main stack is still null! only when thread_1 exists the constructor code it assigns the reference.
See comments below and OP elaborated question.
Lazy<T>
is a very good choice for Thread-Safe Initialization. I think it should be left to the consumer to provide that:
var queue = new Lazy<ThreadSafeQueue<int>>(() => new ThreadSafeQueue<int>());
Parallel.For(0, 10000, i =>
{
else if (i % 2 == 0)
queue.Value.Enqueue(i);
else
{
int item = -1;
if (queue.Value.TryDequeue(out item) == true)
Console.WriteLine(item);
}
});
In answer to your simplified question:
ConcurrentQueue<int> queue = null;
Parallel.Invoke(
() => queue = new ConcurrentQueue<int>(),
() => queue?.Enqueue(5));
It is definitely possible that your code could try to call queue.Enqueue(5)
before queue
has a value, but it isn't anything you could protect against from within the constructor of Queue
. queue
won't actually be assigned a reference to the new instance until the constructor completes.
Unrelated, but still interesting that in Java
for all final fields that are written inside the constructor there would two fences written after the constructor exists: StoreStore
and LoadStore
- that would make publishing the reference thread-safe.