Делегат является неизменным, поэтому при вызове делегата список подписчиков известен и фиксирован. Подписка или отмена подписки заменяет делегата, который поддерживает событие.
Это действительно означает, что в многопоточном сценарии вы можете получить событие после отказа от подписки, потому что либо:
- делегат уже находился в процессе вызова
- снимок делегата уже был получен для цели вызова
2, я имею в виду обычный шаблон (для предотвращения нулевого ref во время вызова):
var handler = SomeEvent;
// <===== another thread could unsubscribe at this point
if(handler != null) handler(sender, args); // <== or part way through this invoke
// (and it either case, have the event trigger even though they think they have
// unsubscribed)
По этой причине, если вы кодируете сложный многопоточный код с событиями, вы должны защищаться таким образом, чтобы запуск события после , который, по вашему мнению, вы отменили, не был проблемой.
Эти нюансы не влияют на однопоточный код.