2010-01-20 6 views
10

Я знаю, что модель памяти .NET (на .NET Framework, а не compact/micro/silverlight/mono/xna/what-have-you) гарантировала, что для определенных типов (в первую очередь примитивных целых и ссылок) операции были гарантированы быть атомарным.Модель памяти .NET, изменчивые переменные и тестовый набор: что гарантировано?

Кроме того, я считаю, что инструкция по тестированию и установке x86/x64 (и Interlocked.CompareExchange) фактически ссылается на глобальную ячейку памяти, поэтому, если ей удастся, другой Interlocked.CompareExchange увидит новое значение.

Наконец, я считаю, что volatile ключевым словом является инструкцией к компилятору распространять читает & пишет как можно скорее и не изменять порядок операций в отношении этого переменного (правильно?).

Это приводит к нескольким вопросам:

  1. ли мои убеждения выше правильно?
  2. Interlocked.Read не имеет перегрузки для int, только для длин (которые являются 2 WORD и, следовательно, обычно не читаются атомарно). Я всегда предполагал, что модель памяти .NET гарантирует, что самое новое значение будет видно при чтении int/ссылок, однако с кэшами процессора, регистрами и т. Д. Я начинаю видеть, что это может быть невозможно. Итак, есть ли способ заставить переменную повторно отобразиться?
  3. Является ли летучесть достаточным для решения вышеуказанной проблемы для целых чисел и ссылок?
  4. На x86/x64 можно предположить, что ...

Если есть две глобальные целочисленные переменные х и у, как инициализируется 0, что если я пишу:

x = 1; 
y = 2; 

Что NO нить будет видеть x = 0 и y = 2 (т. е. записи будут происходить по порядку). Изменяется ли это, если они нестабильны?

ответ

6
  • Только считывает и записывает переменные шириной не более 32 бит (и шириной 64 бит в системах x64) являются атомарными. Все это означает, что вы не будете читать int и получите полузаписанное значение. Это не означает, что арифметика является атомной.
  • Блокированные операции также действуют как барьеры памяти, поэтому да, Interlocked.CompareExchange увидит обновленное значение.
  • См. this page. Летучие не означает упорядоченные. Некоторые компиляторы могут не переупорядочивать операции с изменчивыми переменными, но ЦП может свободно переупорядочивать. Если вы хотите остановить CPU от переупорядочения инструкций, используйте (полный) барьер памяти.
  • Модель памяти гарантирует, что чтение и запись являются атомарными, и использование ключевого слова volatile гарантирует, что чтение будет всегда происходит из памяти, а не из регистра. Итак, вы будете см. Новейшее значение. Это связано с тем, что процессоры x86 при необходимости недействительны для кэширования - см. this и this. Кроме того, см. InterlockedCompareExchange64 о том, как атомно читать 64-битные значения.
  • И, наконец, последний вопрос.Ответ - это поток, который на самом деле может видеть x = 0 и y = 2, а использование ключевого слова volatile не изменится, потому что CPU может свободно переупорядочивать инструкции. Вам нужен барьер памяти.

Резюме:

  1. компилятор волен изменить порядок инструкций.
  2. ЦП может свободно перенаправлять инструкции.
  3. Чтение и запись в формате Word являются атомарными. Арифметические и другие операции не являются атомарными, потому что они включают чтение, вычисление, а затем запись.
  4. Word-размерные данные из памяти всегда будут извлекать новейшее значение. Но большую часть времени вы не знаете, действительно ли вы читаете из памяти.
  5. Пункты блокировки полной памяти (1) и (2). Большинство компиляторов позволяют вам остановить (1) самостоятельно.
  6. Ключевое слово volatile гарантирует, что вы читаете из памяти - (4).
  7. Блокированные операции (префикс блокировки) позволяют выполнять несколько операций с атомами. Например, чтение + запись (InterlockedExchange). Или прочитайте + сравните + напишите (InterlockedCompareExchange). Они также действуют как барьеры памяти, поэтому (1) и (2) останавливаются. Они всегда пишут в память (очевидно), поэтому (4) обеспечивается.
+2

Проблема со ссылкой на «C Keyword Myths Dispelled» в чем-то об .NET заключается в том, что основным источником мифов о «volatile» являются люди, действующие так, как если бы они были одинаковыми в C, C# и Java. В C# 'volatile' действительно есть семантика упорядочения по http://msdn.microsoft.com/en-us/library/aa645755%28v=VS.71%29.aspx. –

+0

"нить на самом деле может видеть x = 0 и y = 2" - поскольку операции записи .NET 2.0 не могут быть переупорядочены. – Vlad

0

Нет, волатильное ключевое слово и гарантия атомарности слишком слабые. Для этого вам нужен барьер памяти. Вы можете получить его явно с помощью метода Thread.MemoryBarrier().

+0

Хорошо, что отвечает 2 и 3, но как насчет №4 (порядок, в котором потоки будут видеть данные)? –

+0

Также из: http://msdn.microsoft.com/en-us/library/x13ttww7%28VS.71%29.aspx (lol msdn urls): «Система всегда считывает текущее значение изменчивого объекта в точке он запрашивается, даже если предыдущая инструкция запрашивала значение от одного и того же объекта. Кроме того, значение объекта записывается сразу при назначении. Модификатор volatile обычно используется для поля, к которому обращаются несколько потоков без использования блокировки оператор для сериализации доступа. Использование изменчивого модификатора гарантирует, что один поток извлекает самое современное значение, написанное другим потоком ». –

+0

Ох и пример кода в http://msdn.microsoft.com/en-us/library/aa645755% 28VS.71% 29.aspx - Кажется, это противоречит вашему ответу. –

2

Пошел через эту старую нить. Ответы от Hans и wj32 правильны, за исключением части, касающейся volatile.

В частности относительно вашего вопроса

На x86/x64 можно предположить, что ... Если есть две глобальные целочисленные переменные х и у, как инициализируется 0, что если я пишу: x = 1; y = 2;

Это нить будет видеть x = 0 и y = 2 (т.е. записи будут происходить в порядке). Это меняется, если они неустойчивы?

Если y летуч, запись в x гарантия произойдет перед записью в y, поэтому ни один поток не увидит x = 0 и y = 2. Это связано с тем, что запись в изменчивую переменную имеет «семантику выпуска» (логически эквивалентную эмиссии заборного забора), то есть все инструкции чтения/записи до того, как они не будут перемещаться. (Это означает, что если x является изменчивым, но y нет, вы все равно можете увидеть неожиданные x = 0 и y = 2.) Подробнее см. Описание & в примере C# spec.

Смежные вопросы