Threading – understanding the volatile modifier in C#

You can become a serverless blackbelt. Enrol to my 4-week online workshop Production-Ready Serverless and gain hands-on experience building something from scratch using serverless technologies. At the end of the workshop, you should have a broader view of the challenges you will face as your serverless architecture matures and expands. You should also have a firm grasp on when serverless is a good fit for your system as well as common pitfalls you need to avoid. Sign up now and get 15% discount with the code yanprs15!

Whilst working on Bingo.Net I have come across a number of concurrency issues which must be fairly common in synchronous multiplayer games. During my attempts to overcome these obstacles I came across the volatile modifier in C# which at first glance looked like a silverbullet for all concurrency problems, but under closer inspection it more closely resembles a false dawn and is catered for a far more cornered use case…

Before we start, let’s first take a look at the definition of the volatile modifier on MSDN:

Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

In other words, by simply marking a field with the volatile keyword, we can be sure that we will always get the latest value of that field wherever we ask for it! Sounds too good to be true? That’s because it is.

The true face of the volatile modifier

Whilst MSDN’s definition is correct it’s certainly misleading. What the volatile modifier gives you is protection against concurrency issues arising from multiple threads executing on multiple CPUs caching data and re-ordering instructions. Now let’s take a closer look at each case:

Data Caching

As Jon Skeet pointed out in his article (see references section), memory in modern computers is complex, with multiple levels of caching, processor registers and multiple processors sharing main memory but possibly not caches, etc. A processor might cache a piece of data from the main memory and use the cached data in the execution of a thread, modify it, and only update the main memory at a later time. During which time another thread running concurrently on another CPU might have read the same bit of data from main memory and used the outdated version of the data, i.e.:


Marking a field as volatile would make sure that it is not cached during the execution of a thread.

Compiler Optimization

The .NET compiler is allowed to alter the order of reads and writes in any way which does not change the meaning of the original program. For example, as pointed out in Dr Dobb’s articled (see reference section) it’s legal for the compiler to transform:

a = 1;  // A

a = 2;  // B


// A’ – eliminate line A

a = 2;  // B

This is legal because the semantic of the original program is preserved. SImilarly:

a = 1;          // C – write to a

local = a;  // D – read from a


a = 1;          // C – write to a

local = 1;  // D’ – apply “constant propagation”

This reordering is also legal according to the rules of the compiler. However, it’s easy to see how this can be a problem in a multi-threaded environment where another thread is writing to a concurrently.


It’s easy to be lured into a false sense of security by the volatile keyword and think that if you’re guarantee the latest value of a field you won’t need to use lock anymore. Marking a field as volatile doesn’t prevent multiple threads from interleaving , take this code for example:

private volatile int _counter = 0;

if (_counter < 10) // guaranteed latest value of _counter
    // value of _counter could have been modified by another thread,
    // hence invalidating the if condition

Also, although you’re able to use the volatile modified on reference types, only the reference (which is a 32-bit integer pointing to a location in memory) itself is volatile but not the instance values. For example, you can’t use volatile on a double because it’s 64-bit and therefore read/write operations cannot be performed atomically (in a single instruction), but you can still make a wrapper class volatile:

public class MyClass
    private volatile VolatileDouble _volatileDouble; // guaranteed

public class VolatileDouble
    public double Double { get; set; }  // not guaranteed

In this case, although you’re guaranteed the latest version of the reference pointer _volatileDouble, you’re not guaranteed the latest value of the Double property of the instance. To get around this, you will need to make the VolatileDouble type immutable so the value of its Double property cannot be changed without creating a new instance and updating the reference point _volatileDouble. However, in situations like this, it’s usually better and easier to use Interlocked.Read and Interlocked.Exchange in conjunction with BitConverter.Int64BitsToDouble and BitConverter.DoubleToInt64Bits instead.

Parting Thoughts..

Though the volatile modifier might not be the all-conquering solution to concurrency, the point to take away from here is that although code like this works most of the time:

class BackgroundTaskDemo
    private bool stopping = false;

    static void Main()
        BackgroundTaskDemo demo = new BackgroundTaskDemo();
        new Thread(demo.DoWork).Start();
        demo.stopping = true;

    static void DoWork()
        while (!stopping)
            // Do something here

it’s not guaranteed to work, and we shouldn’t rely on the right combination of compiler, runtime, JIT, the CPU and the platform to ensure the correct running of our application. This is where the volatile modifier can help.

Also, the volatile modifier can help you achieve thread-safety in some situations. Unlike the bit of unsafe code I showed earlier, where you’re modifying the value of a field based on what it is right now, which requires you to lock around it. (and everywhere else you’re using the same field!) If all you want to do is do a check against a field and then perform an action which does not require using the field again then you can get away with just making it volatile:

private volatile int _counter = 0;
if (_counter < 10) // guaranteed latest value of _counter
    // you're safe so long you don't reference _counter again in here, that
    // goes to any method you call from here too!
    Console.WriteLine("We're still not there yet!");


Jon Skeet’s article on Volatility, Atomicity and Interlocking

Dr Dobb’s article on volatile in different languages

StackOverflow question on volatile vs Interlocked vs lock

StackOverflow question on using volatile and lock

StackOverflow question on using volatile on reference type holding a double

MSDN article on the volatile modifier

Liked this article? Support me on Patreon and get direct help from me via a private Slack channel or 1-2-1 mentoring.
Subscribe to my newsletter

Hi, I’m Yan. I’m an AWS Serverless Hero and I help companies go faster for less by adopting serverless technologies successfully.

Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.

Hire me.

Skill up your serverless game with this hands-on workshop.

My 4-week Production-Ready Serverless online workshop is back!

This course takes you through building a production-ready serverless web application from testing, deployment, security, all the way through to observability. The motivation for this course is to give you hands-on experience building something with serverless technologies while giving you a broader view of the challenges you will face as the architecture matures and expands.

We will start at the basics and give you a firm introduction to Lambda and all the relevant concepts and service features (including the latest announcements in 2020). And then gradually ramping up and cover a wide array of topics such as API security, testing strategies, CI/CD, secret management, and operational best practices for monitoring and troubleshooting.

If you enrol now you can also get 15% OFF with the promo code “yanprs15”.

Enrol now and SAVE 15%.

Check out my new podcast Real-World Serverless where I talk with engineers who are building amazing things with serverless technologies and discuss the real-world use cases and challenges they face. If you’re interested in what people are actually doing with serverless and what it’s really like to be working with serverless day-to-day, then this is the podcast for you.

Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. We will also cover latest features from re:Invent 2019 such as Provisioned Concurrency and Lambda Destinations. Enrol now and start learning!

Check out my video course, Complete Guide to AWS Step Functions. In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. There is something for everyone from beginners to more advanced users looking for design patterns and best practices. Enrol now and start learning!