Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

230
Views
Why use Volatile.Write in Double-Check Locking?

Below is some code from a C# book to show how Singleton pattern is constructed in multithreading:

internal sealed class Singleton {
   // s_lock is required for thread safety and having this object assumes that creating
   // the singleton object is more expensive than creating a System.Object object
   private static readonly Object s_lock = new Object();

   // This field will refer to the one Singleton object
   private static Singleton s_value = null; 

   // Private constructor prevents any code outside this class from creating an instance
   private Singleton() {
      // Code to initialize the one Singleton object goes here...
   }

   // Public, static method that returns the Singleton object (creating it if necessary)
   public static Singleton GetSingleton() {
      // If the Singleton was already created, just return it (this is fast)
      if (s_value != null) return s_value;

      Monitor.Enter(s_lock); // Not created, let 1 thread create it

      if (s_value == null) {
         // Still not created, create it
         Singleton temp = new Singleton();

         // Save the reference in s_value (see discussion for details)
         Volatile.Write(ref s_value, temp); 
      }
      Monitor.Exit(s_lock);

      // Return a reference to the one Singleton object
      return s_value;
   }
}

I get the idea why the code does:

Singleton temp = new Singleton();
Volatile.Write(ref s_value, temp);

instead of

s_value = new Singleton();

because the compiler can allocate memory for the Singleton, assign the reference into s_value, and then call the constructor. From a single thread's perspective, changing the order like this has no impact. But if after publishing the reference into s_value and before calling the constructor, another thread calls the GetSingleton method, then thread will see that s_value is not null and start to use the Singleton object, but its constructor has not finished executing yet.

But I don't understand why we have to use Volatile.Write, can't we do:

Singleton temp = new Singleton();
s_value = temp;

The compiler cannot reorder e.g execute s_value = temp first then execute Singleton temp = new Singleton(), because temp have to exist before s_value = temp?

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

This code is from CLR via C# by Jeffrey Richter.

The explanation from the author in the book (as referred to in the 'see discussion for details' comment) is that Volatile.Write:

ensures that the reference in temp can be published into s_value only after the constructor has finished executing.

Chris Brumme wrote in 2003 about an identical C# double-check pattern (variable names changed):

It works just fine on X86. But it would be broken by a legal but weak implementation of the ECMA CLI spec.

Assume that a series of stores have taken place during construction of [Singleton]. Those stores can be arbitrarily reordered, including the possibility of delaying them until after the publishing store which assigns the new object to [s_value]. At that point, there is a small window before the store.release implied by leaving the lock. Inside that window, other CPUs can navigate through the reference [s_value] and see a partially constructed instance.

So the Volatile.Write is only required when coding for a (theoretical) weakest possible implementation of the ECMA CLI standard memory model.

N.B. that the CLI standard calls out the need for barriers in this case (12.6.8):

It is explicitly not a requirement that a conforming implementation of the CLI guarantee that all state updates performed within a constructor be uniformly visible before the constructor completes. CIL generators may ensure this requirement themselves by inserting appropriate calls to the memory barrier or volatile write instructions.

I've not been able practically demonstrate this problem (visibility of a partially constructed instance due to reordering) with current versions of .net on x64 / ARM64 but I've not tried very hard.

TLDR: writing this sort of code is complicated, use Lazy<T>

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!