Reputation: 1213
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:
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.
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
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