Frage

Here is what MSDN says about volatile:

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. 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.

The volatile keyword can be applied to fields of these types: Reference types.

This state implies that fields of reference type are not volatile by default.
I think it's ok to treat reference-type field as a field of value type containing the address of the object. Then it becomes similar to int type. Joe Albahari gives some examples.

But!... Unlike usual value types GC moves objects into memory when it compacts the heap and changes the references accordingly. Hence 'the most up-to-date value' must always be there. And if so how does the concept of volatility apply to reference types?

War es hilfreich?

Lösung

This state implies that fields of reference type are not volatile by default.

Sure. No field is treated as volatile by default because volatile comes together with the possibility of a sizable performance cost.

I think it's ok to treat reference-type field as a field of value type containing the address of the object. Then it becomes similar to int type.

Assume this can be done without problems. So what? Fields of scalar types such as int or bool are also not treated as volatile by default.

Unlike usual value types GC moves objects into memory when it compacts the heap and changes the references accordingly. Hence 'the most up-to-date value' must always be there. And if so how does the concept of volatility apply to reference types?

You are slightly confused about the utility of volatile. The problem it is meant to address is not only that (A) the most up to date value is not there (although volatile semantics do guarantee that any writes to the value will be observable by any reads that follow them in an abstract timeline¹).

Apart from that, it is also meant to address the situation (B) where the compiler assumes that the code it generates is the only party modifying that value (or the value is not being modified at all), which means that it will not choose to read the value from the field and instead use a "cached copy" it already has at hand. If the value is being modified by a third party at the same time this is obviously going to result in the program using the wrong data for its calculations.

For more information you can refer to this excellent analysis by Igor Ostrovsky, wherein (A) is referred to as "processor optimizations" and (B) as "compiler optimizations".


¹ Please note that this is not a rigorous definition but rather just a crude approximation.

Andere Tipps

Volatile IS useful for reference fields.

Consider this program:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Flag
    {
        public volatile bool Value;
    }

    sealed class Program
    {
        private void run()
        {
            flag.Value = true;
            Task.Factory.StartNew(resetFlagAfter1s);
            int x = 0;

            while (flag.Value)
                ++x;

            Console.WriteLine("Done");
        }

        private void resetFlagAfter1s()
        {
            Thread.Sleep(1000);
            flag = new Flag();
            flag.Value = false;
        }

        private Flag flag = new Flag();

        private static void Main()
        {
            new Program().run();
        }
    }
}

If you run a RELEASE (not debug) build of this, it will never terminate (Visual Studio 2013, .Net 4.5x)

If you change the declaration of flag to:

private volatile Flag flag = new Flag();

Then the release build WILL terminate. This proves that volatile can be significant for a reference field.

I think that Jon answered this question.

I would add that the "only" thing that volatile keyword does is to add an hidden attribute on a field, and to alter the way code is compiled (to IL).

For instance:

    static int x = 0;
    public static void Main(string[] args)
    {
        if (x == 0)
            x++;
    }

is compiled to

L_0000: ldsfld int32 Tests.Program::x
L_0005: brtrue.s L_0013
L_0007: ldsfld int32 Tests.Program::x
L_000c: ldc.i4.1 
L_000d: add 
L_000e: stsfld int32 Tests.Program::x
L_0013: ret 

If you take a look at this part:

ldsfld x
brtrue.s L_0013
ldsfld x

(literally "load a field, if zero, jump. else, load it again")

The first two will translate to something like this in "pseudo asembler"

  • load value of x in a register (mov)
  • if zero, jump elsewhere (jz)

For the third, I guess that the CLR will be tempted to say "hey, I already know that my register contains the value of x, why not skipping the third instruction ?"

... "volatile" keyword prevents this kind of optimizations.

change x definition to static volatile int x=0, and you get:

L_0000: volatile. 
L_0002: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) Tests.Program::x
L_0007: brtrue.s L_0019
L_0009: volatile. 
L_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) Tests.Program::x
L_0010: ldc.i4.1 
L_0011: add 
L_0012: volatile. 
L_0014: stsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) Tests.Program::x
L_0019: ret 

(same thing with reference fields)

See Opcodes.Volatile :

Specifies that an address currently atop the evaluation stack might be volatile, and the results of reading that location cannot be cached or that multiple stores to that location cannot be suppressed.

I recomand you to read Hans Passant excelent answer here

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top