Search code examples
c#multithreadingthread-safetyvolatilelock-free

If I populate a dictionary and assign it to a field - can I guarantee the field will not contain a half populated dictionary?


In the below code, I populate the contents of the dictionary then assign it to Data. As such, my mental expectation is that Data will contain either null or a Dictionary with two elements in it.

But is that a valid expectation?

I know that writes can be reordered. And the simple fix, presumably, would be to add volatile to Data. But is that needed? Or is the original code guaranteed "safe" (safe in the sense that I can be 100% confident that Data will never contain a Dictionary that has < 2 elements in it).

using System;
using System.Collections.Generic;

public class Program
{
    public static Dictionary<int, int> Data = null;
    
    public static void Main()
    {
        var bob = new Dictionary<int, int>();
        bob.Add(1, 1);
        bob.Add(2, 2);
        
        Data = bob;

        // Note that there be no further mutation of `bob` or `Data` after this.

        // Imagine there is a separate thread reading the contents of `Data`
    }
}

I am not even super confident of the volatile suggestion in light of the wisdom of Skeet and Lippert ("Frankly, I discourage you from ever making a volatile field.").


Solution

  • I know that writes can be reordered. And the simple fix, presumably, would be to add volatile to Data. But is that needed?

    Yes, it is definitely needed. Without the volatile the compiler/Jitter/processor might reorder the instructions of the program in a way that the Data might point for a brief period of time to a partially initialized Dictionary<K,V> object. The behavior of a partially initialized object is undefined. It might contain less than 2 elements, it might throw exceptions, whatever. Or it might work correctly because no reordering actually happened. If you want to be responsible and leave nothing to chance, you should definitely declare the Data as volatile, so that any possibility of reordering is prevented.

    A detailed explanation about why the volatile is needed in lazy-initialization scenarios, can be found in this 2013 article by Igor Ostrovsky: