2008-09-25 6 views
87

Я хотел бы закодировать небольшую программу, которая наглядно иллюстрирует поведение ключевого слова volatile. В идеале это должна быть программа, которая выполняет параллельный доступ к нестабильному статическому полю и из-за этого получает неправильное поведение.Иллюстрирование использования ключевого слова volatile в C#

Добавление ключевого слова volatile в ту же программу должно устранить проблему.

Этого я не смог достичь. Даже пытаясь несколько раз, обеспечивая оптимизацию и т. Д., Я всегда получаю правильное поведение без ключевого слова «volatile».

У вас есть какие-либо идеи по этой теме? Вы знаете, как имитировать такую ​​проблему в простом демонстрационном приложении? Это зависит от аппаратного обеспечения?

ответ

97

Я достиг рабочего примера!

Основная идея получена от wiki, но с некоторыми изменениями для C#. Статья в wiki демонстрирует это для статического поля C++, похоже, что C# всегда тщательно компилирует запросы на статические поля ...и я сделать пример с не статичный:

Если запустить этот пример в Release режиме и без отладчика (то есть с помощью Ctrl + F5), то линия while (test.foo != 255) будет оптимизирована для «в то время (правда)» и эта программа никогда не возвращается. Но после добавления ключевого слова volatile вы всегда получаете «ОК».

class Test 
{ 
    /*volatile*/ int foo; 

    static void Main() 
    { 
     var test = new Test(); 

     new Thread(delegate() { Thread.Sleep(500); test.foo = 255; }).Start(); 

     while (test.foo != 255) ; 
     Console.WriteLine("OK"); 
    } 
} 
21

Да, это зависит от оборудования (вы вряд ли увидите проблему без нескольких процессоров), но она также зависит от реализации. Спецификации модели памяти в спецификации CLR допускают те вещи, которые реализация CLR для Microsoft не обязательно делает. Лучшая документация, которую я видел по ключевому слову volatile, - this blog post by Joe Duffy. Обратите внимание, что он говорит, что документация MSDN «сильно вводит в заблуждение».

+8

http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/ – Xaqron 2010-10-26 00:52:49

+1

следить за Джо Даффи на 'летучего' ключевое слово. http://www.bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx – 2012-05-22 16:12:37

6

На самом деле дело не в ошибке, когда ключевое слово «volatile» не указано, более того, ошибка может произойти, если она не указана. Как правило, вы узнаете, когда это будет лучше, чем компилятор!

Самый простой способ думать об этом заключается в том, что компилятор мог бы, если бы захотел, установить определенные значения. Пометив значение как изменчивое, вы сообщаете себе и компилятору, что значение может фактически измениться (даже если компилятор так не считает). Это означает, что компилятор не должен указывать значения в строке, хранить кеш или читать значение раньше (в попытке оптимизировать).

Это не похоже на то же ключевое слово, что и на C++.

В MSDN есть короткое описание here. Вот, возможно, более глубокий пост по предметам Volatility, Atomicity and Interlocking

4

Это трудно продемонстрировать в C#, как код отвлекается на виртуальной машине, при этом на одном из вариантов осуществления этой машины она работает прямо без изменчивы, в то время как он может потерпеть неудачу на другом.

The Wikipedia has a good example how to demonstrate it in C, though.

То же самое может произойти и в C#, если JIT компилятор решает, что значение переменной не может измениться в любом случае, и, таким образом, создает машинный код, который даже не проверить его больше. Если теперь другой поток меняет значение, ваш первый поток все равно может быть пойман в цикле.

Another example is Busy Waiting.

Опять же, это может произойти с C#, а также, но это сильно зависит от виртуальной машины и на JIT компилятор (или интерпретатор, если он не имеет JIT ... в теории, я думаю, MS всегда использует компилятор JIT, а также Mono использует его, но вы можете его отключить вручную).

4

Вот мой вклад в коллективное понимание этого поведения ... Это не так много, просто демонстрация (на основе демо xkip в), который показывает поведение летучих стихов энергонезависимого (т.е. «нормальный») int value, бок о бок, в той же программе ... что я искал, когда нашел эту нить.

using System; 
using System.Threading; 

namespace VolatileTest 
{ 
    class VolatileTest 
    { 
    private volatile int _volatileInt; 
    public void Run() { 
     new Thread(delegate() { Thread.Sleep(500); _volatileInt = 1; }).Start(); 
     while (_volatileInt != 1) 
     ; // Do nothing 
     Console.WriteLine("_volatileInt="+_volatileInt); 
    } 
    } 

    class NormalTest 
    { 
    private int _normalInt; 
    public void Run() { 
     new Thread(delegate() { Thread.Sleep(500); _normalInt = 1; }).Start(); 
     // NOTE: Program hangs here in Release mode only (not Debug mode). 
     // See: http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp 
     // for an explanation of why. The short answer is because the 
     // compiler optimisation caches _normalInt on a register, so 
     // it never re-reads the value of the _normalInt variable, so 
     // it never sees the modified value. Ergo: while (true)!!!! 
     while (_normalInt != 1) 
     ; // Do nothing 
     Console.WriteLine("_normalInt="+_normalInt); 
    } 
    } 

    class Program 
    { 
    static void Main() { 
#if DEBUG 
     Console.WriteLine("You must run this program in Release mode to reproduce the problem!"); 
#endif 
     new VolatileTest().Run(); 
     Console.WriteLine("This program will now hang!"); 
     new NormalTest().Run(); 
    } 

    } 
} 

Есть несколько действительно отличных кратких объяснений выше, а также некоторые замечательные ссылки. Спасибо всем за то, что помогли мне окунуться в volatile (по крайней мере, чтобы знать, не полагайтесь на volatile, где мой первый инстинкт был lock).

Приветствия, и спасибо за ВСЕ рыбу. Кит.


PS: Я был бы очень заинтересован в демо исходном запросе, который был: «Я хотел бы видеть в статического летучего INT ведет себя правильно, где статических INT. ведет себя плохо

Я пытался и не на этот вызов. (на самом деле я сдался довольно быстро ;-). Во всем, что я пытался со статическими переменными они ведут себя «правильно», независимо от того, или нет, вы их volatile ... и мне хотелось бы объяснить ПОЧЕМУ, что это так, если это действительно так ... Неужели компилятор не кэширует значения значений статических варов в регистрах (т. он кэширует ссылку на на этот куча-адрес)?

Нет, это не новый вопрос ... это попытка стереть сообщество назад в исходный вопрос.

2

Я натолкнулся на следующий текст Джо Альбахари, который мне очень помог.

Я захватил пример из приведенного выше текста, который я изменил немного, путем создания статического летучий поля. Когда вы удаляете ключевое слово volatile, программа будет блокироваться бесконечно. Запустите этот пример в версии.

class Program 
{ 
    public static volatile bool complete = false; 

    private static void Main() 
    {   
     var t = new Thread(() => 
     { 
      bool toggle = false; 
      while (!complete) toggle = !toggle; 
     }); 

     t.Start(); 
     Thread.Sleep(1000); //let the other thread spin up 
     complete = true; 
     t.Join(); // Blocks indefinitely when you remove volatile 
    } 
}