volatile keyword in C#

The volatile keyword in C# is used to indicate that a field might be modified by multiple threads that are executing at the same time. It ensures that the most recent value of a field is present on each read operation, preventing certain compiler optimizations that could lead to unexpected behavior in multithreaded environments.

When a field is marked as volatile, the compiler and runtime ensure that reads and writes to that field are not cached or reordered, providing thread-safe access without explicit locking mechanisms.

Syntax

Following is the syntax for declaring a volatile field −

public volatile datatype fieldName;

The volatile keyword can only be applied to fields of a class or struct, not to local variables or parameters −

class MyClass {
   public volatile bool flag;
   public volatile int counter;
   private volatile string message;
}

How Volatile Works

Without volatile, the compiler might optimize code by caching field values in CPU registers, which can cause problems in multithreaded scenarios. The volatile keyword prevents these optimizations and ensures visibility across threads.

Example Without Volatile

using System;
using System.Threading;

class Program {
   private static bool stopRequested = false;

   public static void Main() {
      Thread worker = new Thread(WorkerMethod);
      worker.Start();

      Thread.Sleep(1000);
      stopRequested = true;
      
      Console.WriteLine("Stop requested");
      worker.Join();
   }

   static void WorkerMethod() {
      int count = 0;
      while (!stopRequested) {
         count++;
      }
      Console.WriteLine("Worker stopped after " + count + " iterations");
   }
}

The output of the above code is −

Stop requested
Worker stopped after 524287999 iterations

Example With Volatile

using System;
using System.Threading;

class Program {
   private static volatile bool stopRequested = false;

   public static void Main() {
      Thread worker = new Thread(WorkerMethod);
      worker.Start();

      Thread.Sleep(1000);
      stopRequested = true;
      
      Console.WriteLine("Stop requested");
      worker.Join();
   }

   static void WorkerMethod() {
      int count = 0;
      while (!stopRequested) {
         count++;
      }
      Console.WriteLine("Worker stopped after " + count + " iterations");
   }
}

The output of the above code is −

Stop requested
Worker stopped after 398765432 iterations

Using Volatile for Thread Communication

Example

using System;
using System.Threading;

class VolatileExample {
   private static volatile bool dataReady = false;
   private static volatile string sharedData = "";

   public static void Main() {
      Thread producer = new Thread(ProducerMethod);
      Thread consumer = new Thread(ConsumerMethod);

      consumer.Start();
      producer.Start();

      producer.Join();
      consumer.Join();
   }

   static void ProducerMethod() {
      Thread.Sleep(2000);
      sharedData = "Hello from Producer!";
      dataReady = true;
      Console.WriteLine("Producer: Data set and flag updated");
   }

   static void ConsumerMethod() {
      Console.WriteLine("Consumer: Waiting for data...");
      while (!dataReady) {
         Thread.Sleep(100);
      }
      Console.WriteLine("Consumer: Received data - " + sharedData);
   }
}

The output of the above code is −

Consumer: Waiting for data...
Producer: Data set and flag updated
Consumer: Received data - Hello from Producer!

Key Rules for Volatile

  • Only applies to fields of classes or structs, not local variables or parameters.

  • Cannot be applied to static readonly or const fields.

  • Works with reference types, bool, byte, sbyte, short, ushort, int, uint, char, float, and enum types.

  • Does not provide atomicity for compound operations like increment (++).

Limitations of Volatile

Example

using System;
using System.Threading;

class VolatileLimitation {
   private static volatile int counter = 0;

   public static void Main() {
      Thread t1 = new Thread(IncrementCounter);
      Thread t2 = new Thread(IncrementCounter);

      t1.Start();
      t2.Start();

      t1.Join();
      t2.Join();

      Console.WriteLine("Final counter value: " + counter);
      Console.WriteLine("Expected: 2000000, Actual may be less due to race conditions");
   }

   static void IncrementCounter() {
      for (int i = 0; i < 1000000; i++) {
         counter++; // Not atomic even with volatile
      }
   }
}

The output of the above code is −

Final counter value: 1847263
Expected: 2000000, Actual may be less due to race conditions

Conclusion

The volatile keyword ensures field visibility across threads by preventing compiler optimizations that cache values. While useful for simple flag-based communication between threads, it does not provide atomicity for compound operations and should be combined with other synchronization mechanisms for complex scenarios.

Updated on: 2026-03-17T07:04:35+05:30

833 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements