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

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

  • Правило 1: зависимость данных от нагрузок и магазины никогда не нарушаются.
  • Правило 2: все магазины имеют семантику освобождения , т. е. никакая нагрузка или магазин не могут двинуть после один.
  • Правило 3: все летучие нагрузки приобрести, т. е. не загружать или хранить может двигайся до часу.
  • Правило 4: отсутствие нагрузок и магазины могут когда-либо пересечь полный барьер (например, нить.Перед вызовом memorybarrier, замок приобретайте, блокируйте.Обмен, Сблокированный.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. Кто-нибудь хочет просветить меня (или исправить меня или добавить что-то к любому из вышеперечисленного?)

1 ответов


Джо Даффи обсуждает Правило 5 на pp517-18 параллельное программирование на Windows:

в качестве примера, когда нагрузка может быть введено, рассмотрим этот код:

MyObject mo = ...;
int f = mo.field;
if (f == 0)
{
    // do something
    Console.WriteLine(f);
}

если период времени между начальным читайте МО.поле в переменную F и последующее использование f в Приставка.WriteLine был достаточно длинным, a компилятор может решить, что это будет больше эффективно перечитывать МО.поле дважды. ... Делающий это было бы проблемой, если mo-объект кучи, а потоки писать одновременно МО.поле. Этот если блок может содержать код, который предполагает значение, считанное в f, осталось 0, и введение чтения может нарушить это предположение. В дополнение к запрещение этого для volatile переменные, модель памяти .NET запрещает для обычных переменных ссылаясь на память кучи GC тоже.

Я блог об одном важном месте, где это имеет значение: в стандартный шаблон для поднятия события.

EventHandler handler = MyEvent;
if (handler != null)
    handler(this, EventArgs.Empty);

чтобы предотвратить проблемы с удалением обработчика событий в отдельном потоке, мы читаем текущее значение MyEvent и вызывать обработчики событий, только если этот делегат не равен null.

если чтение из кучи может быть введено, компилятор / JIT может решить, что лучше читать MyEvent опять же, вместо того, чтобы использовать локальный, который ввел бы условие гонки.