2017-01-30 37 views
4

Пусть следующий код (обратите внимание, что это только пример кода):Использование Volatile.Write() вместо летучих в C#

public async void UnsafeMethod() 
{ 
    int unsafeValue = 0; 

    Task.Run(() => unsafeValue = 42); // set unsafeValue to some value 
    await Task.Delay(10);    // wait for some time 

    Assert.AreEqual(42, unsafeValue); // check if unsafeValue has the new value 
} 

(Предположим здесь, что процессор находится в режиме ожидания и немедленно выполняет задачу .)

Поскольку задача будет выполнена в новом потоке, новое значение unsaveValue может быть не видно другим темам из-за возможных проблем с кешированием. Если я хочу, чтобы сделать изменения видимыми, я бы использовать volatile и сделать локальную переменную поле: (. Опять Предположим, что задача выполняется немедленно)

private volatile int safeValue = 0; 

public async void SafeMethod() 
{ 
    Task.Run(() => safeValue = 42); // set safeValue to some value 
    await Task.Delay(10);   // wait for some time 

    Assert.AreEqual(42, safeValue); // check if safeValue has the new value 
} 

Мой вопрос сейчас, если следующий будет делать то же самое, сохраняя при этом локальном переменном (я знаю, что компилятор делает это поле в любом случае ...):

public async void AnotherMethod() 
{ 
    int safeValue = 0; 

    Task.Run(() => Volatile.Write(ref safeValue, 42); // set safeValue to some value 
    await Task.Delay(10);        // wait for some time 

    Assert.AreEqual(42, safeValue);     // check if safeValue has the new value 
} 

(Task получает немедленно казнен.)

Документация мне не совсем понятна. https://msdn.microsoft.com/en-us/library/system.threading.volatile(v=vs.110).aspx

Она гласит:

На многопроцессорной системе, летучий операция записи гарантирует, что значение, записанное в ячейку памяти сразу виден всем процессоров.

Это то, что я хочу. Однако в нем также указывается:

Записывает указанное значение в указанное поле. В системах, которые требуется , вставляет защитный барьер памяти, который препятствует процессору от операций переупорядочивания памяти следующим образом: если чтение или запись появляется до этого метода в коде, процессор не может переместить его после этого метода.

в описании метода, которое я не уверен, что это то, что я хочу.

Снова мой вопрос: будет ли последний отрезанный код делать то, что я хочу его сделать (сделать запись сразу видимой для других потоков)?

+3

Вы определенно хотите использовать 'Volatile.Write()' вместо 'volatile'. Фактически, [Эрик Липперт говорит, что вам следует избегать «volatile») (https://blogs.msdn.microsoft.com/ericlippert/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three /). –

+4

IMHO, 'Assert.AreEqual (42, safeValue);' не знает о вызове 'Volatile.Write', поэтому' safeValue' может быть кэширован. Я думаю, что 'Volatile.Read' (когда вы вызываете' Assert.AreEqual') ... –

+2

Matteo правильный - вам нужно использовать 'Volatile.Write()' для каждой записи и 'Volatile.Read()' для чтения (для поля, совместно используемого потоком). –

ответ

-1

Как Маттео Umili и Мэтью Уотсон упоминается в комментариях, вы должны использовать Volatile.Read, когда вы читаете энергонезависимую поле. Это потому, что синхронизация всегда является игрой в 2 игрока.

В этом случае Volatile.Write вставляет барьер-хранилище после записи, а Volatile.Read устанавливает барьер перед приобретением.

+0

Я до сих пор не уверен, включена ли «синхронизация» на аппаратной части (т. Е. Если кэши будут немедленно возвращены в память). С точки зрения программного обеспечения это работает. Однако я не уверен, как гарантировать или проверить, что физическое назначение memeory на самом деле не кэшировано. –

+0

Что происходит в аппаратных средствах, зависит от JIT и конкретного процессора. Даже если вы используете 'Interlocked.MemoryBarrier', который в настоящее время переводится в LOCKed-операцию, которая мигает буферами записи, это не гарантирует, что другие процессоры будут видеть записи« немедленно »и принимать их во внимание, поскольку перед ними есть буферы чтения из них. Поэтому не думайте об этом на аппаратном уровне. Летучие - это атомарность и барьеры памяти. Подумайте, как обращения к памяти связаны друг с другом и как препятствия препятствуют их переупорядочению. – OmariO