J’ai donc lu cela au lieu d’appeler un événement directement avec
if (SomeEvent != null) SomeEvent(this, null);
je devrais faire
SomeEventHandler temp = SomeEvent; if (temp != null) temp(this, null);
Pourquoi cela est-il ainsi? Comment la deuxième version devient-elle thread-safe? Quelle est la meilleure pratique?
Les événements sont vraiment du sucre syntaxique sur une liste de delegates. Lorsque vous appelez l’événement, il s’agit en fait de parcourir la liste et d’appeler chaque délégué avec les parameters que vous avez passés.
Le problème des threads est qu’ils pourraient append ou supprimer des éléments de cette collection en s’abonnant / désabonnant. S’ils le font pendant que vous parcourez la collection, cela causera des problèmes (je pense qu’une exception est levée)
L’intention est de copier la liste avant de l’itérer, de sorte que vous êtes protégé contre les modifications de la liste.
Remarque: Il est toutefois maintenant possible que votre auditeur soit appelé même après votre désinscription. Vous devez donc vous assurer que vous gérez cela dans votre code d’auditeur.
OMI, les autres réponses manquent un détail clé: les delegates (et donc les événements) sont immuables . Cela signifie que souscrire ou désabonner un gestionnaire d’événements ne consiste pas simplement à append / supprimer une liste, mais à la remplacer par un nouvel élément comportant un élément supplémentaire (ou un élément de moins).
Étant donné que les références sont atomiques, cela signifie qu’au point que vous faites:
var handler = SomeEvent;
vous avez maintenant une instance rigide qui ne peut pas changer, même si à la picoseconde suivante, un autre thread se désabonne (le champ de l’événement réel devient alors null
).
Donc, vous testez null et vous l’invoquez, et tout va bien. Notez bien sur qu’il y a toujours le scénario déroutant de l’événement levé sur un object qui pense qu’il s’est désabonné il y a une picoseconde!
La meilleure pratique est la deuxième forme. La raison en est qu’un autre thread peut avoir la valeur null ou modifier SomeEvent
entre le test ‘ if
‘ et l’invocation.
Voici un bon article sur les événements .NET et les conditions de course avec des threads. Il couvre certains scénarios courants et contient de bonnes références.
J’espère que cela t’aides.