Это зависит от того, что вы подразумеваете под потокобезопасностью. Если ваше определение включает в себя предотвращение NullReferenceException
, то первым примером является more safe. Однако, если вы переходите к более строгому определению, в котором обработчики событий должны быть вызываться, если они существуют, то ни один из них не является безопасным. Причина связана с сложностями модели памяти и барьеров. Может быть, на самом деле есть обработчики событий, привязанные к делегату, но поток всегда читает ссылку как null. Правильный способ исправить это - создать явный барьер памяти в точке, где ссылка делегата будет записана в локальную переменную.
lock
(или любой механизм синхронизации). volatile
для переменной события. Thread.MemoryBarrier
. Несмотря на проблему неудобного обзора, которая мешает вам выполнять однострочный инициализатор, я по-прежнему предпочитаю метод lock
.
protected virtual void OnSomethingHappened(EventArgs e)
{
EventHandler handler;
lock (this)
{
handler = SomethingHappened;
}
if (handler != null)
{
handler(this, e);
}
}
важно отметить, что в данном конкретном случае проблема барьер памяти, вероятно, спорный вопрос, потому что маловероятно, что читает переменные будут сняты вне вызовов методов. Но нет гарантии, особенно если компилятор решает встроить метод.