Should thread-safe class have a memory barrier at the end of its constructor?

◇◆丶佛笑我妖孽 提交于 2019-11-27 15:02:56

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);
    }
});

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.

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!