2010-01-08 4 views
4

J'ai suivi quelques tutoriels sur la façon de créer des accesseurs d'événement custem. Voici le code que j'ai:C# Pourquoi ma déclaration de verrouillage est-elle suspendue?

event ControlNameChangeHandler IProcessBlock.OnControlNameChanged 
{ 
    add 
    { 
     lock (ControlNameChanged) 
     { 
      ControlNameChanged += value; 
     } 
    } 
    remove 
    { 
     lock (ControlNameChanged) 
     { 
      ControlNameChanged -= value; 
     } 
    } 
} 

Au moment le code atteint lock(ControlNameChanged) dans le statament add, rien ne se passe. Le code ne fonctionne plus. Cependant, mon application fonctionne toujours. Il ne gèle pas ou quelque chose.

Qu'est-ce qui ne va pas?

+1

changer le gestionnaire pendant l'exécution du gestionnaire est une très mauvaise idée! –

+0

Comment voulez-vous dire? Désolé, je suis nouveau à cela. – Martijn

+0

@Mitch: ressemble plus à une référence à un délégué ou à un autre événement sur le même objet, puisqu'il s'agit d'une implémentation d'interface explicite. – Lucero

Répondre

13

D'une certaine manière, quelqu'un d'autre détient un verrou. Vous ne devez pas utiliser d'instances ou d'événements de délégué de multidiffusion pour le verrouillage, et vous ne devez pas non plus utiliser de membres publics, car vous ne pouvez pas contrôler qui verrouille et quand.

donc j'utiliser un objet de verrouillage séparé comme celui-ci:

private readonly object controlNameChangedSync = new object(); 

event ControlNameChangeHandler IProcessBlock.OnControlNameChanged 
{ 
    add 
    { 
    lock (controlNameChangedSync) 
    { 
     ControlNameChanged += value; 
    } 
    } 
    remove 
    { 
    lock (controlNameChangedSync) 
    { 
     ControlNameChanged -= value; 
    } 
    } 
} 

Note: la référence à l'événement change lorsque vous faites un += ou -= le délégué.

+0

En effet (ma réponse maintenant supprimée dit la même chose, fondamentalement). Le code actuel n'est pas thread-safe. –

+0

Leur verrou implicite n'est-il pas si vous omettez l'ajout/le retrait? Si vous n'avez rien à ajouter ou à supprimer, laissez C# faire le verrouillage. Votre article 'lockchoice' vous dites que vous recommandez de tordre les événements avec ajouter/supprimer toujours mais vous ne dites pas pourquoi. –

+0

@Lucero: Je ne comprends pas. Pourquoi devrais-je utiliser la serrure? Si je commente la déclaration de verrouillage tout fonctionne bien. Pouvez-vous me donner des informations générales sur votre solution? – Martijn

4

Les opérateurs += et -=modifient le délégué. Vos méthodes Add et Remove se verrouillent donc sur des objets différents à chaque fois, et vous n'avez aucune synchronisation. Maintenant, cela n'expliquerait pas le blocage, mais votre question n'est pas très claire sur ce qui se passe réellement. Je m'attendrais à ce que le programme s'exécute "normalement" avec la possibilité de conditions de course.

8

Votre code est équivalent à

event ControlNameChangeHandler IProcessBlock.OnControlNameChanged { 
    add { 
     try { 
      Monitor.Enter(ControlNameChanged); 
      ControlNameChanged = ControlNameChanged + value; 
     } 
     finally { 
      Monitor.Exit(ControlNameChanged); 
     } 
    } 
    remove { 
     try { 
      Monitor.Enter(ControlNameChanged); 
      ControlNameChanged = ControlNameChanged - value; 
     } 
     finally { 
      Monitor.Exit(ControlNameChanged); 
     } 
    } 
} 

Notez que l'objet sortie est différent de celui que vous entrez. Cela signifie que vous avez pris un verrou qui n'est jamais libéré, et qu'un verrou est libéré mais jamais pris. Vous devriez changer votre code en ceci:

private object padlock = new object(); 
event ControlNameChangeHandler IProcessBlock.OnControlNameChanged { 
    add { 
     lock (padlock) { 
      ControlNameChanged += value; 
     } 
    } 
    remove { 
     lock (padlock) { 
      ControlNameChanged -= value; 
     } 
    } 
} 
+0

Bonne explication! Je ne l'ai pas compris avant de lire votre message. – leiflundgren