2011-04-17 3 views
2

j'ai découvert ce post sur StackOverflow sur les événements et les courses qui m'a énormément aidé -Plus d'informations sur les événements et la sécurité des threads

C# Events and Thread Safety

La déclaration clé ici est que « Les gestionnaires d'événements doivent être robustes face J'entends par là que lorsque vous vous abonnez à un événement, vous devez vous préparer à ce que cet événement soit déclenché même après vous être désabonné, et faire une sorte de tri. de vérifier pour voir si l'événement devrait être traité.

Cela pourrait être quelque chose aussi simple (et laid) comme

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); 

Maintenant, évidemment, le code ci-dessus est un dieu terrible à regarder, mais il sert l'objectif requis. Ma question est, quelle serait une façon plus élégante de le faire? Quelle est la manière typique de gérer cette situation?

Je pensais que vous pourriez peut-être faire

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

mais j'ai essayé, et je suppose que les événements sont conçus de manière à vous empêcher de voir d'autres abonnés.

Qu'en pensez-vous?

Merci

+0

Vos _acceptEvents vous semblent OK et aussi élégants que possible. –

+0

Ce n'est pas techniquement correct, une barrière de mémoire est requise. Cela marchera cependant, il y en a un implicite dans l'événement non-abonnement. Déclarer la variable * volatile * est suffisant. –

+0

Barrière de mémoire nécessaire pour quoi exactement? – NoPyGod

Répondre

1

imho c'est un problème inexistant. Avez-vous déjà eu un code qui a généré un bug (ou va générer un bug) parce qu'un événement a été appelé après qu'il a été désabonné?

+0

Le code sur lequel je travaille maintenant pourrait très probablement rencontrer ce problème.Je travaille sur le code socket, et il y a des points où demander à la socket de se fermer, et que le serveur ferme le socket pour vous, pourrait très probablement se produire à proximité. – NoPyGod

+0

Je pense que la meilleure solution peut être de gérer le cas où le socket a déjà commencé à être fermé. Cela arrivera relativement rarement. Et de toute façon, les événements externes peuvent provoquer des modifications sur le socket, vous devez donc coder toutes les interactions avec le socket de manière défensive. –

+0

En ce qui concerne les sockets, il peut avoir été fermé à tout moment par une défaillance du réseau, par le point de terminaison distant ou pour toute autre raison que vous ne pouvez pas contrôler. Ce que je dis, c'est que le socket peut être invalide pour un certain nombre de raisons, et l'événement ne sera pas le plus gros problème. – jgauffin

0

événements En effet encapsulent une liste d'appel de délégué multicast de sorte que vous ne pouvez pas supprimer directement votre abonnement. La principale raison pour laquelle un gestionnaire peut être appelé après la désinscription (même si vous pouvez supprimer votre abonnement de la liste) est que le propriétaire de l'événement a pu faire une copie de la liste d'invocation.

Regardez ce code:

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

La première ligne crée une copie de la liste d'appel pour éviter de soulever l'événement dans la 4e ligne sur une liste vide (et ainsi vous protéger d'une exception de référence null Par conséquent, si vous annulez l'enregistrement entre les lignes 1 et 4, votre gestionnaire sera toujours appelé car une référence à celui-ci a été copiée dans ev.

+0

Oui, je comprends parfaitement cela. Ma question est, comment un abonné d'événement code-il élégamment avec ce gotcha particulier à l'esprit? – NoPyGod

+0

Tout type de drapeau devrait faire. Juste faire attention à la lecture d'un réglage du drapeau de plusieurs threads. –

Questions connexes