Создание событий в многопоточной среде [дубликат]
На этот вопрос уже есть ответ здесь:
Начиная с .NET 4.0 автоматически генерируемые обработчики событий add/remove являются потокобезопасными (здесь и здесь). Поэтому клиенты, которые регистрируют своих слушателей в открытом событии, могут делать это одновременно из нескольких потоков без гонок.
Но что, если я хочу запустить событие потокобезопасным способом? Рекомендуемая практика, по-видимому, заключается в следующем (здесь):
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
if (myEvent != null)
{
myEvent(this, e);
}
}
Однако, прочитав что-то о модели памяти .NET (например, журнал MSDN2012-12 и еще 2013-01) я больше не думаю, что это правильно. Меня беспокоит, что чтение памяти может быть введено компилятором, и поэтому приведенный выше код может быть преобразован в нечто подобное:
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
// JIT removed the local variable and introduced two memory reads instead.
if (MyEvent != null)
{
// A race condition may cause the following line to throw a NullReferenceException.
MyEvent(this, e);
}
}
Законно: удалите локальную переменную и используйте повторные операции чтения памяти, так как это не изменяет поведение метода, если он выполняется в однопоточной среде. Это по спецификации ECMA (ECMA-335: I. 12.6.4). Понятный пример также приводится в 2013-01 выпуск журнала MSDN.
Я что-то упустил? Если нет, то, пожалуйста, посоветуйте обходной путь.
1 ответ:
Вы должны добавить единственную строку, чтобы сделать первый фрагмент правильным в многопоточной среде:
public event EventHandler MyEvent; protected void OnMyEvent(EventArgs e) { EventHandler myEvent = MyEvent; Thread.MemoryBarrier(); if (myEvent != null) { myEvent(this, e); } }
Барьер памяти отказывается переупорядочивать чтение и запись как для компилятора, так и для процессора. Именно так реализуются операции чтения/записи с изменяющимися параметрами. Вы можете прочитать больше о барьере памяти здесь.