Cabbage Champion
Cabbage Champion

Reputation: 1213

Reading and writing to boxed double values thread safe with no lock?

From the words of MS, reads and writes (along with other operations) to doubles are not atomic and thus not thread safe. I wanted to see if I can make reads and writes to double thread safe by boxing the double values and making the reference to the boxed value volatile. Below is a class that demonstrates my use of boxed doubles across a worked and consumer thread:

public class Program {
    public volatile static object objCounter;
    public static AutoResetEvent Finish = new AutoResetEvent(false);
    static void Main(string[] args) {
        // Worker thread that writes to the boxed double values
        Task.Run(() => {
            double dCounter = 0d;
            while (!Finish.WaitOne(100, false)) {
                dCounter += 1.5d;

                // Box dCounter into objCounter -
                // Copy double value to new location on the heap
                // and point the objCounter to this location on the heap
                objCounter = (object)dCounter; 
            }
        });

        // Consumer thread that reads the boxed double values
        for (int index = 0; index < 50; index++) {
            Console.WriteLine(String.Format("Double counter value = {0}",objCounter));
            Thread.Sleep(100);
        }
        Finish.Set();
    }
}

I believe the above code is thread safe for the following reasons:

  1. When the worker thread writes the double to objCounter, it must create a new location on the heap, copy the double value to that new location on the heap, then atomically copy the refence to the new heap location to objCounter. This atomic copy of the refence(pointer) to objCounter is guaranteed to be atomic straight from the mouth of MS: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#96-atomicity-of-variable-references – (See 9.6 Atomicity of variable references) It is important to note that a new location on the heap is created for every time we box the double, if we didn’t create a new location on the heap than this would not be thread safe.

  2. Since objCounter is volatile, there would be no memory visibility issues between the worker and consumer threads of the reference that points to the memory location on heap that contains the current double value.

Am I correct to assume my use of boxed doubles across a worker and consumer thread as specified above is thread safe?

Upvotes: 2

Views: 197

Answers (1)

Theodor Zoulias
Theodor Zoulias

Reputation: 43485

Yes, your code is thread-safe. Since the objCounter is a volatile field, the two lines below:

objCounter = (object)dCounter;
Console.WriteLine(objCounter);

...are equivalent to those:

Volatile.Write(ref objCounter, (object)dCounter);
Console.WriteLine(Volatile.Read(ref objCounter));

Both Volatile methods create memory barriers. The Volatile.Write ensures that the processor will not move the instructions assosiated with creating the objCounter instance, after this call. And the Volatile.Read ensures that the processor will not move the instructions assosiated with displaying the objCounter in the console, before this call. So the thread that displays the objCounter will always see a fully initialized instance.

Upvotes: 2

Related Questions