Joe Duffy, дает 6 правил, которые описывают CLR 2.0 + модель памяти (это - фактическая реализация, не любой стандарт ECMA), я записываю свою попытку понимания этого, главным образом как способ резинового ныряния, но если я сделаю ошибку в своей логике, то по крайней мере кто-то здесь сможет поймать его, прежде чем это вызовет меня горе.
Я пытаюсь понять эти правила.
x = y
y = 0 // Cannot move before the previous line according to Rule 1.
x = y
z = 0
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load 0
store z
Смотря на это, кажется, что загрузка 0 может быть перемещена до перед загрузкой y, но хранилища не могут быть переупорядочены вообще. Поэтому, если поток будет видеть z == 0, то он также будет видеть x == y.
Если бы y был энергозависим, то загрузитесь 0, то не мог бы переместиться перед загрузкой y, иначе он может. Энергозависимые хранилища, кажется, не имеют специальных свойств, никакие хранилища не могут быть переупорядочены друг относительно друга (который является очень сильной гарантией!)
Полные барьеры похожи на строку в песке, который загружается, и хранилища не могут быть отодвинуты.
Никакая идея, что означает правило 5.
Я предполагаю средства правила 6, если Вы делаете:
x = y
x = z
Затем для CLR возможно удалить и загрузку в y и первое хранилище к x.
x = y
z = y
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load y
store z
// could be re-ordered like this
load y
load y
store x
store z
// rule 6 applied means this is possible?
load y
store x // but don't pop y from stack (or first duplicate item on top of stack)
store z
Что, если y был энергозависим? Я не вижу ничего в правилах, которое запрещает вышеупомянутую оптимизацию от того, чтобы быть выполненным. Это не нарушает перепроверяемую блокировку, потому что блокировка () между двумя идентичными условиями препятствует тому, чтобы загрузки были перемещены в смежные положения, и согласно правилу 6, это - единственное время, они могут быть устранены.
Таким образом, я думаю, что понимаю всех кроме правила 5, здесь. Кто-либо хочет просветить меня (или исправить меня или добавить что-то к какому-либо вышеупомянутому?)
Джо Даффи обсуждает Правило 5 на стр. 517-18 из Параллельное программирование в Windows :
В качестве примера, когда загрузка возможно введены, рассмотрите этот код:
MyObject mo = ...;
int f = mo.field;
if (f == 0)
{
// do something
Console.WriteLine(f);
}
Если период времени между начальными чтение mo.field в переменную f и последующее использование f в Console.WriteLine была достаточно длинной, компилятор может решить, что будет больше Эффективно перечитать mo.field дважды. ... Это будет проблемой, если mo - это объект кучи, а потоки одновременная запись в mo.field. В if-block может содержать код, предполагающий значение, считываемое в f, осталось 0, и введение чтения может сломаться это предположение. В добавление к запрещая это для летучих переменные, модель памяти .NET запрещает это для обычных переменных относится и к памяти кучи GC.
Я писал об одном важном месте, где это имеет значение : стандартном паттерне для создания события.
EventHandler handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
Чтобы предотвратить проблемы с удалением обработчика событий в отдельном потоке, мы читаем текущее значение MyEvent
и вызываем обработчики событий только в том случае, если этот делегат не равен нулю.
Если бы можно было ввести чтение из кучи, компилятор / JIT мог бы решить, что было бы лучше прочитать MyEvent
снова, а не использовать локальное, что привело бы к возникновению состояния гонки.