2011-09-28 1 views
2

Je travaille sur une application multithread qui doit mettre à jour un contrôle Winforms à mesure qu'il progresse (un DataGridView). Afin d'empêcher l'accès multiple à la même ressource partagée, j'ai commencé avec la structure de verrouillage suivante:Utilisation de Control.Invoke() à la place du verrou (Control)

if (DGV.Columns.Count >= DesiredColumnCount) return; 
lock(DGV) 
{ 
    while (DGV.Columns.Count < DesiredColumnCount) 
    { 
     DGV.Columns.Add(newColumn); 
    } 
} 

Je me suis alors rendu compte que, depuis le DGV a été créé sur le thread d'interface utilisateur, il doit être .Invoke() « d . Cela change le code à:

if (DGV.Columns.Count >= DesiredColumnCount) return; 
lock(DGV) 
{ 
    DGV.Invoke(new MethodInvoker(() => 
     { 
      while (DGV.Columns.Count < DesiredColumnCount) 
      { 
       DGV.Columns.Add(newColumn); 
      } 
     } 
} 

Ma question est: Est-ce que ce n'est pas redondant? le lock bloquera le thread de travail jusqu'à ce qu'il ait un accès exclusif à DGV, et le Invoke() bloquera le thread de travail jusqu'à ce que le thread de l'interface utilisateur puisse prendre la demande d'invocation et exécuter le code. Je ne peux pas juste obtenir en utilisant seulement le Invoke()?

(Telle est la question principale. Bien sûr, s'il y a d'autres péchés multithreading dans le code ci-dessus, s'il vous plaît un commentaire)

+0

Qu'est-ce que vous utilisez pour un "thread de travail"? –

+0

Vous ne devez jamais bloquer le thread d'interface utilisateur. Cela vaut pour Lock ainsi que Control.Invoke. Vous devriez vous débarrasser du verrou et changer Invoke en BeginInvoke. –

+0

@PeterRitchie Je ne me souviens pas exactement de ce qui a commencé le fil. C'était probablement 'new Thread(). Start()' ou 'System.Threading.Timer.Elapsed'. Ce code n'aurait pas bloqué le thread d'interface utilisateur, il aurait bloqué le travailleur. BeginInvoke ne peut pas être substitué aveuglément à Invoke car il ne bloque pas l'appelant: je devrais mettre en file d'attente les invocations pour m'assurer qu'elles ne se chevauchent pas pour être exécutées dans le mauvais ordre. Cela peut augmenter les performances en réduisant le temps passé à bloquer les threads de travail, mais c'est un ancien projet à ce jour. – Nathan

Répondre

3

Il est un peu redondant, l'appel Invoke sera « expédition » le operpation à l'interface utilisateur fil. Chaque appel Invoke suivant sera envoyé en série, il n'y a donc pas vraiment besoin de verrouiller.

Vous pouvez envisager d'utiliser BeginInvoke au lieu de Invoke afin d'empêcher le thread de travail de bloquer, mais encore une fois, cela sera fait "en série", donc il n'y a pas besoin de s'inquiéter du verrouillage.

+0

Merci pour le poste, mais je suis confus. M. Skeet dit dans (http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke) que Control.Invoke s'exécute sur le thread d'interface utilisateur et attend la fin. – Nathan

+0

Oui, il parle de BeginInvoke(). Peu importe, vous n'avez pas besoin d'une serrure de toute façon. Tout le code s'exécute sur le même thread, il n'est donc pas nécessaire de protéger les données. –

+2

Non seulement ce verrou est redondant, mais c'est une impasse qui risque de se produire. Si un code appelé lors de cet appel à 'Invoke' essaie d'obtenir le verrou, vous êtes bloqué parce que votre thread maintient le verrou, attendant que' Invoke' se termine sur le thread de l'interface utilisateur et le thread de l'interface utilisateur attend pour acquérir le verrou. –

Questions connexes