Понимание модели памяти CLR 2.0

Joe Duffy, дает 6 правил, которые описывают CLR 2.0 + модель памяти (это - фактическая реализация, не любой стандарт ECMA), я записываю свою попытку понимания этого, главным образом как способ резинового ныряния, но если я сделаю ошибку в своей логике, то по крайней мере кто-то здесь сможет поймать его, прежде чем это вызовет меня горе.

  • Правило 1: зависимость Данных среди загрузок и хранилищ никогда не нарушается.
  • Правило 2: Все хранилища имеют семантику выпуска, т.е. никакая загрузка или хранилище не могут переместиться после один.
  • Правило 3: Все энергозависимые загрузки, получают, т.е. никакая загрузка или хранилище не могут переместиться прежде один.
  • Правило 4: Никакие загрузки и хранилища никогда не могут пересекать полный барьер (например, Поток. MemoryBarrier, блокировка получает, Взаимно блокируемый. Exchange, Взаимно блокируемый. CompareExchange, и т.д.).
  • Правило 5: Загрузки и хранилища к "куче" никогда не могут представляться.
  • Правило 6: Загрузки и хранилища могут только быть удалены при объединении смежных загрузок и хранилищ из/в то же местоположение.

Я пытаюсь понять эти правила.

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, здесь. Кто-либо хочет просветить меня (или исправить меня или добавить что-то к какому-либо вышеупомянутому?)

13
задан Joel Coehoorn 31 May 2010 в 03:41
поделиться

1 ответ

Джо Даффи обсуждает Правило 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 снова, а не использовать локальное, что привело бы к возникновению состояния гонки.

10
ответ дан 2 December 2019 в 01:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: