Course contentsShow
← C#
Lesson 66 of 1,9081. C# Language FoundationsFree lesson

Volatile Memory Access

Ensure multi-thread visibility for shared fields bypassing CPU caches.

Understanding Volatile Memory Access

In this lesson, you will learn how to ensure that multiple threads always see the most up-to-date value of a shared variable by bypassing CPU caching optimizations.

The Problem: Invisible Updates

When you write a program that uses multiple threads, your computer’s CPU and the .NET runtime often try to be "smart" to improve performance. To keep things fast, a CPU might store the value of a shared field in a local processor cache rather than reading it from main RAM every single time.

Imagine Thread A updates a flag to true, but Thread B—running on a different CPU core—still sees the old false value because it is reading from its own local cache. To Thread B, it’s as if the update never happened. This is a classic concurrency bug where data visibility is "stale."

The Solution: The volatile Keyword

In C#, you can use the volatile keyword on a field to tell the compiler and the CPU: "Do not cache this value." By marking a field as volatile, you are explicitly telling the system that this variable might be modified by multiple threads at any time.

When a field is volatile, the CPU is forced to perform a "memory fence." This ensures that every read of the variable comes directly from the main memory, and every write is immediately committed to main memory.

public class StoppableWorker
{
    // Without 'volatile', the worker thread might 
    // cache 'shouldStop' and never see the change!
    private volatile bool _shouldStop = false;

    public void Stop() => _shouldStop = true;

    public void DoWork()
    {
        while (!_shouldStop)
        {
            // Perform work...
        }
    }
}

Important Limitations

volatile is not a magic fix for all threading problems. It does not provide atomicity for complex operations (like incrementing a counter), and it doesn’t replace the need for locks or synchronization primitives when managing complex state. It simply guarantees that a thread will not see a stale, cached version of a simple field. It is a targeted tool for high-performance, low-level state flags.

Key Takeaway: Use the volatile keyword on shared fields to force threads to bypass CPU caches, ensuring they always read the most recent value from main memory.