Подробнее о событиях и безопасности потоков - PullRequest
2 голосов
/ 17 апреля 2011

Я обнаружил этот пост в StackOverflow о событиях и гонках, которые мне очень помогли -

C # События и безопасность потоков

Ключевым утверждением здесь является то, что «обработчики событий должны быть устойчивыми при вызове даже после того, как событие было отписано»

Я полагаю, это означает, что когда вы подписываетесь на событие, вы должны быть готовы к тому, что это событие будет инициировано даже после того, как вы отменили подписку, и выполнить какую-то проверку, чтобы увидеть, следует ли обрабатывать событие.

Это может быть что-то столь же простое (и безобразное), как

bool _acceptEvents;

// event handler
void LoggedIn(object sender, EventArgs a)
{
    if (!_acceptEvents) return;

    Evt("Now logged in");
}

// code to unsubscribe to event
_acceptEvents = false;
_parent.LoggedIn -= new LoggedInEventHandler(LoggedIn);

Теперь очевидно, что вышеприведенный код ужасен, но он служит нужной цели.

У меня вопрос: что может быть более элегантным способом сделать это? Каков типичный способ справиться с этой ситуацией?

Я думал, что вы могли бы сделать

if (!_parent.LoggedIn.Contains(myhandler)) return;

но я пытался, и я думаю, что события разработаны таким образом, чтобы вы не могли видеть других подписчиков.

Что вы думаете?

Спасибо

Ответы [ 2 ]

1 голос
/ 17 апреля 2011

imho это несуществующая проблема. Был ли у вас когда-нибудь код, который сгенерировал ошибку (или сгенерирует ошибку), потому что событие было вызвано после того, как оно было отписано?

0 голосов
/ 17 апреля 2011

Действительно, события инкапсулируют список вызовов многоадресного делегата, поэтому вы не можете напрямую удалить свою подписку.

Основная причина того, что обработчик может быть вызван после отмены регистрации (даже если вы можете удалить свою подписку из списка), заключается в том, что владелец события мог сделать копию списка вызовов.

Посмотрите на этот код:

var ev = TheEvent;
if(ev != null)
{
    ev(this, EventArgs.Empty);
}

В первой строке создается копия списка вызовов, чтобы предотвратить появление события в 4-й строке в пустом списке (и тем самым защитить вас от исключения нулевой ссылки)

Таким образом, если вы отмените регистрацию между строками 1 и 4, ваш обработчик все равно будет вызван, потому что ссылка на него была скопирована в ev.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...