BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

C# Programming
.NET 1.1+

C# Volatile Fields

Optimisation techniques used when compiling or running software that uses the .NET framework can lead to unexpected results. One situation that causes problems is performing non-volatile reads of fields in multithreading or parallel programming scenarios.

Non-Volatile Reads

Software built on the .NET framework is subject to many optimisations. Some optimisation is performed when compiling your program or library in Visual Studio or using the command-line compiler. Other optimisations are applied when executing the compiled intermediate language (IL) code. These vary according to the type of processor used to run the program. In many situations these optimisations lead to faster code or smaller programs without any noticeable side effects.

One optimisation that can have side effects relates to publicly visible fields in classes or structures. When you request the value of such a field the program normally performs a non-volatile read. This type of read can be optimised to improve the performance of the program. For example, the processor may choose to read the value from memory earlier than expected, and potentially in a different order than specified, in preparation for its later use. This may move the value to the processor's cache memory, where it can be accessed more quickly than from the main memory, or to its registers for yet faster performance. In single-threaded code these changes are unnoticeable.

When you are creating a multithreaded application or one that uses parallel programming code, non-volatile reads can present a problem. To illustrate this, consider the following program:

using System.Threading;

class Program
{
    static void Main()
    {
        StatusChecker checker = new StatusChecker();
        checker.StatusUpdated = false;

        new Thread(new ThreadStart(() => checker.UpdateStatus())).Start();

        while (!checker.StatusUpdated);
            
        Console.WriteLine("Status is {0}", checker.Result);
    }
}


class StatusChecker
{
    public string Result;

    public bool StatusUpdated;

    public void UpdateStatus()
    {
        Thread.Sleep(2000);
        Result = "OK";
        Console.WriteLine("Status Obtained");
        StatusUpdated = true;
    }
}

The above code presents a simple example that has a bug caused by a non-volatile read. The StatusChecker class performs some kind of status check when the UpdateStatus method is executed. The Thread.Sleep statement adds a two-second pause that simulates a hardware check. Once complete, the Result property is set to the "OK" and the StatusUpdated flag is set.

In the Main method of the Program class we instantiate a new StatusChecker and set the StatusUpdated property to false. We then start the UpdateStatus method on a new thread so that we can continue processing other code. NB: In the example a lambda expression is used to define the thread's actions. If you are using a version of .NET that does not support lambdas you will need to change this to use a delegate or anonymous method.

After the second thread is launched, a while loop is encountered. This waits for the StatusUpdated flag of the StatusChecker to be set before continuing. The final action is to output the Result property.

At first glance you may expect this program to run correctly. However, there is a problem. When executed with all optimisations enabled the program shows the following output and then hangs indefinitely. NB: To run with all optimisations switch to Release mode and start the program by pressing Ctrl-F5.

Status Obtained

In this case a compiler optimisation causes the failure. As the code sets the initial value of the StatusUpdated field but never again changes it, the compiler removes the references to this field from the Main method completely. The while statement essentially becomes "while (true)", which is an infinite loop.

Volatile Reads

To solve the problem we need to remove the optimisations around the StatusUpdated flag. We can do this using the volatile modifier, which specifies that all reads and writes of the field must be performed as volatile reads and writes. This means that all access to the field will be performed in the order stated in the code and that the values will be read from memory immediately before they are required. It solves the multithreading problem for both compiler and processor optimisations. In our sample program this means that the while loop will read the StatusUpdated flag from memory in each iteration and will exit when the flag becomes true.

To mark a field as volatile, precede its data type with the volatile keyword, as shown below:

public volatile bool StatusUpdated;

If you run the modified code you should see the desired output before the program exits normally:

Status Obtained
Status is OK

Limitations

There are some limitations when using volatile reads. One is that the volatile keyword may only be applied to the following types:

  • Reference types, noting that only the reference is affected. Reads from the object's properties and fields will still be non-volatile unless otherwise configured.
  • The built-in data types, byte, sbyte, short, ushort, int, uint, char, float and bool.
  • Enumerations with a base type of byte, sbyte, short, ushort, int or uint.
  • Pointer types.

When working with other types that require volatile access, surround all reads and writes of the field with a lock statement. You can also use the lock statement for the above mentioned data types.

It is important to note that the volatile keyword does not remove the other synchronisation problems of multithreaded and parallel programming. For example, if aggregating values in a parallel loop you must still use locking to prevent the use of inconsistent values in shared, mutable state.

10 September 2011