Когда использовать volatile для противодействия оптимизации компилятора в C #

Я потратил огромное количество недель, занимаясь многопоточным кодированием на C # 4.0. Однако есть один вопрос, который остался без ответа для меня.

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

Я также знаю, что оптимизация компилятора несколько «непредсказуема». Следующий код иллюстрирует зависание из-за оптимизации компилятора (при запуске релизной компиляции вне VS):

class Test
{
    public struct Data
    {
        public int _loop;
    }

    public static Data data;

    public static void Main()
    {
        data._loop = 1;
        Test test1 = new Test();

        new Thread(() =>
        {
            data._loop = 0;
        }
        ).Start();

        do
        {
            if (data._loop != 1)
            {
                break;
            }

            //Thread.Yield();
        } while (true);

        // will never terminate
    }
}

Код ведет себя как ожидалось. Однако, если я раскомментирую //Thread.Yield (); линия, то цикл завершится.

Кроме того, если я помещу оператор Sleep перед циклом do, он завершится. Я не понимаю

Естественно, декорирование _loop с помощью volatile также приведет к выходу из цикла (в его показанном шаблоне).

Мой вопрос: по каким правилам соблюдается компилятор, чтобы определить, когда бездействию выполнить изменчивое чтение? И почему я все еще могу заставить цикл завершиться с нечетными мерами?

РЕДАКТИРОВАТЬ

IL для кода, как показано (киоски):

L_0038: ldsflda valuetype ConsoleApplication1.Test/Data ConsoleApplication1.Test::data
L_003d: ldfld int32 ConsoleApplication1.Test/Data::_loop
L_0042: ldc.i4.1 
L_0043: beq.s L_0038
L_0045: ret 

IL с выходом () (не останавливается):

L_0038: ldsflda valuetype ConsoleApplication1.Test/Data ConsoleApplication1.Test::data
L_003d: ldfld int32 ConsoleApplication1.Test/Data::_loop
L_0042: ldc.i4.1 
L_0043: beq.s L_0046
L_0045: ret 
L_0046: call bool [mscorlib]System.Threading.Thread::Yield()
L_004b: pop 
L_004c: br.s L_0038

Ответы на вопрос(4)

Ваш ответ на вопрос