Изменение порядка.Net CompareExchange
Может ли компилятор или процессор переупорядочить следующие инструкции, чтобы другой поток видел a == 0
а также b == 1
?
Если предположить, int a = 0, b = 0;
где-то.
System.Threading.Interlocked.CompareExchange<int>(ref a, 1, 0);
System.Threading.Interlocked.CompareExchange<int>(ref b, 1, 0);
3 ответа
Нет. Использование Interlock
будет сигнализировать полный забор памяти. "То есть любая переменная записывает перед вызовом Interlocked
метод выполняется до Interlocked
метод, и любая переменная читает после вызова выполняется после вызова ". [1] Они используют изменчивые методы чтения / записи, чтобы предотвратить b = 1
до a = 1
,
[1]: Джеффри Рихтер: "CLR через C# - третье издание", часть V Threading, стр. 803
Конечно, это возможно. Отдельные операции, составляющие CompareExchange
операция не может быть заметно переупорядочена, но два вызова CompareExchange
может быть переупорядочен с точки зрения другого потока, если поток, выполняющий этот код, не может наблюдать такое поведение.
Инструменты синхронизации на месте для CompareExchange
предотвращают наблюдаемое переупорядочение между операциями, затрагивающими области памяти, относящиеся к этой операции, а не какие-либо операции в целом, и в этом коде нет ничего, что мешало бы компилятору или JITter переупорядочивать эти два CompareExchange
звонки целиком (с точки зрения другого потока).
Вы читаете слишком много теории. Да, это может случиться на практике, если другой поток
Console.WriteLine("a: {0}, b: {1}", a, b);
Поскольку String.Format, который используется для форматирования строки, имеет подпись
String Format(string fmt, params object[] args)
{
....
}
ваши целые числа будут скопированы из-за бокса. Единственное условие, которое должно быть истинным, состоит в том, что отрезок времени потоков заканчивается, когда он скопировал a в своем унифицированном состоянии. Позже, когда поток возобновит работу, обе переменные будут установлены на одну, но ваш вывод на консоль будет
a: 0, b: 1
Точка просмотра других значений происходит немедленно, если вы работаете с копиями значений, не осознавая этого. Вот почему вы обычно разрешаете другим людям писать правильный код без блокировки. Если вы попытаетесь отладить код без блокировки с помощью Console.WriteLine, я желаю вам удачи в этом.
Хотя a и b установлены по порядку (я не думаю, что JIT-компилятор будет переупорядочивать ваши заблокированные вызовы), у вас нет гарантии, что другие потоки увидят только два ноля или два. Вы можете попытаться прочитать сначала b и проверить, имеет ли оно значение 1, затем вы можете вывести значение a, но в этом случае вам даже не понадобится значение a. Это должно быть верно по крайней мере для модели памяти x86. Это предположение может быть нарушено более слабыми моделями памяти, такими как ARM.