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