A reproducible example of volatile usage

前端 未结 2 2069
半阙折子戏
半阙折子戏 2020-11-28 14:47

I am look for a reproducible example that can demonstrate how volatile keyword works. I\'m looking for something that works \"wrong\" without variable(s) marked as volatile

相关标签:
2条回答
  • 2020-11-28 15:12

    The exact semantics of volatile is a jitter implementation detail. The compiler emits the Opcodes.Volatile IL instruction where ever you access a variable that's declared volatile. It does some checking to verify that the variable type is legal, you can't declare value types larger than 4 bytes volatile but that's where the buck stops.

    The C# language specification defines the behavior of volatile, quoted here by Eric Lippert. The 'release' and 'acquire' semantics is something that only makes sense on a processor core with a weak memory model. Those kind of processors have not done well in the market, probably because they are such an enormous pain to program. The odds that your code will ever run on a Titanium are slim to none.

    What's especially bad about the C# language specification definition is that it doesn't mention at all what really happens. Declaring a variable volatile prevents the jitter optimizer from optimizing the code to store the variable in a cpu register. Which is why the code that Marc linked is hanging. This will only happen with the current x86 jitter, another strong hint that volatile is really a jitter implementation detail.

    The poor semantics of volatile has a rich history, it comes from the C language. Whose code generators have lots of trouble getting it right as well. Here's a interesting report about it (pdf). It dates from 2008, a good 30+ years of opportunity to get it right. Or wrong, this goes belly-up when the code optimizer is forgetting about a variable being volatile. Unoptimized code never has a problem with it. Notable is that the jitter in the 'open source' version of .NET (SSLI20) completely ignores the IL instruction. It can also be argued that the current behavior of the x86 jitter is a bug. I think it is, it is not easy to bump it into the failure mode. But nobody can argue that it actually is a bug.

    The writing is on the wall, only ever declare a variable volatile if it is stored in a memory mapped register. The original intention of the keyword. The odds that you'll run into such a usage in the C# language should be vanishingly small, code like that belongs in a device driver. And above all, never assume that it is useful in a multi-threading scenario.

    0 讨论(0)
  • 2020-11-28 15:18

    You can use this example to demonstrate the different behavior with and without volatile. This example must be compiled using a Release build and ran outside of the debugger1. Experiment by adding and removing the volatile keyword to the stop flag.

    What happens here is that the read of stop in the while loop is reordered so that it occurs before the loop if volatile is omitted. This prevents the thread from ending even after the main thread set the stop flag to true.

    class Program
    {
        static bool stop = false;
    
        public static void Main(string[] args)
        {
            var t = new Thread(() =>
            {
                Console.WriteLine("thread begin");
                bool toggle = false;
                while (!stop)
                {
                    toggle = !toggle;
                }
                Console.WriteLine("thread end");
            });
            t.Start();
            Thread.Sleep(1000);
            stop = true;
            Console.WriteLine("stop = true");
            Console.WriteLine("waiting...");
    
            // The Join call should return almost immediately.
            // With volatile it DOES.
            // Without volatile it does NOT.
            t.Join(); 
        }
    }
    

    It should also be noted that subtle changes to this example can reduce its probability of reproducibility. For example, adding Thread.Sleep (perhaps to simulate thread interleaving) will itself introduce a memory barrier and thus the similar semantics of the volatile keyword. I suspect Console.WriteLine introduces implicit memory barriers or otherwise prevents the jitter from using the instruction reordering operation. Just keep that in mind if you start messing with the example too much.


    1I believe that framework version prior to 2.0 do not include this reordering optimization. That means you should be able to reproduce this behavior with version 2.0 and higher, but not the earlier versions.

    0 讨论(0)
提交回复
热议问题