2009-07-20 6 views
1

Nous avons un code en cours d'exécution dans un thread d'arrière-plan qui doit faire apparaître une boîte de dialogue ou d'une autre interaction de l'utilisateur, donc nous l'appel Invoke habituelle sur le thread d'interface utilisateur:Invoke avec délai d'attente

Control.Invoke(SomeFunction); 

void SomeFunction() 
{ 
    ... 
} 

Mais, nous est tombé sur un bug, notre thread UI ne répond parfois pas immédiatement à l'appel Invoke - nous l'avons dépisté au fait que le thread de l'interface utilisateur effectuait actuellement un appel DCOM inter-processus qui n'était pas encore retourné. Une fois l'appel DCOM retourné, notre fonction serait appelée, mais jusque-là il est apparu que l'appel Invoke avait été bloqué.

Ma solution pour cela était d'introduire un délai d'attente:

ManualResetEvent invokeEvent = new ManualResetEvent(); 
var result = Control.BeginInvoke(SomeFunction, invokeEvent); 

if (!invokeEvent.WaitOne(1000)) 
    throw new Exception("Not responding"); 

Control.EndInvoke(result); 

void SomeFunction(ManualResetEvent invokeEvent) 
{ 
    invokeEvent.Set(); 

    ... 
} 

Cela a fonctionné dans les « travaux sur mon sens de la machine », mais il y avait un certain nombre de défauts.

http://www.codinghorror.com/blog/images/works-on-my-machine-stamped.png

  • Tout d'abord la fonction est toujours invoquée, même si le délai d'attente se produit - si l'appel avait pas réellement DCOM complètement accroché, il finira par fonctionner
  • En second lieu, il y a la course terrible évidente état
  • Enfin, il y a toute « Arrgh » ness de la chose

Même si les deux premières choses pourraient être résolus, nous avons encore le gen ickyness eral. Y a-t-il une meilleure façon de résoudre cela?

Répondre

0

Déplacez l'appel DCOM inter-processus vers un autre thread. Vous êtes évidemment suspendus le fil de l'interface utilisateur, ce qui est complètement inacceptable. Corrigez cela et votre problème fantôme (l'OP) disparaît aussi.

+0

Je suis désolé de dire que nous ne possédons pas le code qui rend l'appel DCOM inter-processus. Même si nous le faisions, nous aurions toujours besoin de résoudre ce problème en tant que problème général dans notre propre code car tout autre code pouvant être chargé pourrait causer ce problème général. Bonne idée cependant –

0

Il s'agit d'un problème de threads courant quand il s'agit d'exécuter quelque chose sur le thread graphique et ce symptôme affecte toutes les races de développeurs.

Si vous créiez un thread séparé dans lequel vous affichez la boîte de dialogue de progression réelle et un autre thread pour effectuer l'appel DCOM, cela impliquerait simplement de déplacer la synchronisation ManuaResetEvent entre les deux threads. Cela a l'avantage de ne pas verrouiller le thread graphique car le thread séparé qui crée le formulaire de progression aura sa propre file d'attente de messages créée et le second thread utilisé pour exécuter l'appel DCOM n'aura pas à verrouiller un thread graphique.

Il nécessite une certaine sycnhronizing attention, mais une fois fait, il est beau de voir en action:

private ManualResetEvent _event = new ManualResetEvent(false); 
... 

private void StartTheComProgressCall() 
{ 
    _event.Reset(); 

    ThreadPool.QueueUserWorkItem(StartProgressDialog); 
    ThreadPool.QueueUserWorkItem(StartDCOMCall); 

    // there's various possibilities to perform here, we could ideally 1) wait on the 
    // event to complete, 2) run a callback delegate once everything is done 
    // 3) fire an event once completed 
} 

private void StartProgressDialog(object state) 
{ 
    ProgressDialog dialog = new ProgressDialog(); 
    dialog.Show(); 

    while(!_event.WaitOne(0)) 
     Application.DoEvents(); 

    dialog.Close(); 
} 

private void StartDCOMCall() 
{ 
    ... 
    <perform your DCOM routines here> 

    // once the call is done, remember to trigger that it's complete 
    // so that blocking threads can continue to do what they need to do 
    _event.Set(); 
} 

Remarques Certains pourraient argumenter contre l'utilisation de la méthode Application.DoEvents(), mais considérer que DoEvents oblige tous les messages Windows en attente sur la file d'attente des messages du thread d'appel en cours de traitement et puisque l'appel est fait dans un thread différent (celui qui a créé la boîte de dialogue de progression) et non le thread graphique, il ne devrait pas y avoir . Nous devrions utiliser n'importe quels outils ou techniques pour nous aider à faire le travail.