Здесь есть несколько проблем, и я буду заниматься ими по одному.
Проблема № 1: Ваш код, вам нужно заблокировать?
Прежде всего, код, который у вас есть в вашем вопросе, нет необходимости в блокировке этого кода.
Другими словами, метод Raise можно просто переписать так:
public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
if (handler != null)
handler(sender, e);
}
Причина этого заключается в том, что делегат является неизменяемой конструкцией, что означает, что делегат, который вы получите в свой метод, как только вы будете в нем, не изменится за время существования этого метода.
Даже если другой поток одновременно обрабатывает событие, это создаст нового делегата. Объект делегата, который есть в вашем объекте, не изменится.
Так что это проблема № 1, вам нужно заблокировать, если у вас есть код, как вы сделали. Ответ - нет.
Проблема № 3: Почему выходные данные из вашего последнего фрагмента кода не изменились?
Это восходит к приведенному выше коду. Метод расширения уже получил свою копию делегата, и эта копия никогда не изменится. Единственный способ «изменить его» - это не передавать метод копии, а вместо этого, как показано в других ответах, передать ему псевдоним для поля / переменной, содержащей делегат. Только тогда вы увидите изменения.
Вы можете посмотреть на это следующим образом:
private int x;
public void Test1()
{
x = 10;
Test2(x);
}
public void Test2(int y)
{
Console.WriteLine("y = " + y);
x = 5;
Console.WriteLine("y = " + y);
}
Ожидаете ли вы, что y изменится на 5 в этом случае? Нет, вероятно нет, и то же самое с делегатами.
Проблема № 3: Почему Джон использовал блокировку в своем коде?
Так почему же Джон использовал блокировку в своем посте : Выбор того, что блокировать на ? Ну, его код отличается от вашего в том смысле, что он нигде не передавал копию основного делегата.
В своем коде, который выглядит так:
protected virtual OnSomeEvent(EventArgs e)
{
SomeEventHandler handler;
lock (someEventLock)
{
handler = someEvent;
}
if (handler != null)
{
handler (this, e);
}
}
есть вероятность, что если он не использовал блокировки, а вместо этого написал код, подобный этому:
protected virtual OnSomeEvent(EventArgs e)
{
if (handler != null)
handler (this, e);
}
тогда другой поток может изменить «обработчик» между вычислением выражения, чтобы выяснить, есть ли подписчики, и до фактического вызова, другими словами:
protected virtual OnSomeEvent(EventArgs e)
{
if (handler != null)
<--- a different thread can change "handler" here
handler (this, e);
}
Если бы он передал handler
в отдельный метод, его код был бы аналогичен вашему, и поэтому не требовал блокировки.
По сути, передача значения делегата в качестве параметра делает копию, этот «копируемый» код является атомарным. Если вы правильно рассчитали время для другого потока, этот другой поток либо изменит свое время, чтобы получить новое значение с помощью вызова, либо нет.
Одной из причин использования блокировки даже при вызове может быть введение барьера памяти, но я сомневаюсь, что это повлияет на этот код.
Итак, это проблема №3, почему код Джона действительно нуждался в блокировке.
Проблема № 4: А как насчет изменения методов доступа к событиям по умолчанию?
Проблема 4, которая была затронута в других ответах, здесь связана с необходимостью блокировки при перезаписи средств доступа по умолчанию для добавления / удаления для события, чтобы управлять логикой по любой причине.
В основном вместо этого:
public event EventHandler EventName;
Вы хотите написать это или какой-то другой вариант:
private EventHandler eventName;
public event EventHandler EventName
{
add { eventName += value; }
remove { eventName -= value; }
}
Этот код действительно нуждается в блокировке, потому что если вы посмотрите на оригинальную реализацию, без переопределенных методов доступа, вы заметите, что она использует блокировку по умолчанию, тогда как код, который мы написали, не делает.
Мы можем получить сценарий выполнения, который выглядит следующим образом (помните, что «a + = b» действительно означает «a = a + b»):
Thread 1 Thread 2
read eventName
read eventName
add handler1
add handler2
write eventName
write eventName <-- oops, handler1 disappeared
Чтобы это исправить, вам нужна блокировка.