2010-10-15 15 views
0

J'ai une bibliothèque externe qui a une méthode qui exécute une tâche longue sur un thread d'arrière-plan. Quand c'est fait, il déclenche un événement Completed sur le thread qui a lancé la méthode (généralement le thread de l'interface utilisateur). Il ressemble à ceci:Comment empêcher d'avancer jusqu'à ce qu'une tâche d'arrière-plan soit terminée?

public class Foo 
{ 
    public delegate void CompletedEventHandler(object sender, EventArgs e); 
    public event CompletedEventHandler Completed; 

    public void LongRunningTask() 
    { 
     BackgroundWorker bw = new BackgroundWorker(); 
     bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
     bw.RunWorkerAsync(); 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     Thread.Sleep(5000); 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (Completed != null) 
      Completed(this, EventArgs.Empty); 
    } 
} 

Le code qui appelle cette bibliothèque ressemble à ceci:

private void button1_Click(object sender, EventArgs e) 
{ 
    Foo b = new Foo(); 
    b.Completed += new Foo.CompletedEventHandler(b_Completed); 
    b.LongRunningTask(); 

    Debug.WriteLine("It's all done");  
} 

void b_Completed(object sender, EventArgs e) 
{ 
    // do stuff 
} 

Dans la méthode button1_Click, après que je l'appelle b.LongRunningTask(), l'événement se déclenche Terminé 5 secondes de plus tard sur le thread de l'interface utilisateur, je mets à jour l'interface utilisateur et tout est génial, car je n'ai pas besoin de traiter les trucs de marshaling au thread approprié.

Cependant, j'ai maintenant besoin que le processus soit synchrone (sans changer la bibliothèque externe). En d'autres termes, après avoir lancé la méthode .LongRunningTask, l'instruction significative suivante de cette méthode doit être déclenchée après la fin de .LongRunningTask.

J'ai essayé de le faire avec EventWaitHandle (par exemple en faisant WaitOne après l'appel de LongRunningTask puis en le réinitialisant dans l'événement Completed, mais cela bloque tout).

Y at-il une méthode dans le framework .NET qui me permet de faire cela?

+0

Je voudrais voir le code qui sérialise le rappel au thread principal à partir de la bibliothèque. J'ai essayé de faire de telles choses, et échoué - il me semble que vous devriez envoyer plus de paramètres pour cela - au moins le contrôle qui sera utilisé pour Invoke() plus tard? –

Répondre

4

Je l'ai essayé de le faire avec le EventWaitHandle (par exemple faire WaitOne après l'appel à LongRunningTask puis Remise à zéro en cas terminé, mais que tout se bloque tout vers le haut).

C'est exactement ce qui se passera si vous rendez cette phase synchrone, par définition. Vous ne pouvez pas le rendre synchrone sans bloquer le thread de l'interface utilisateur. Au lieu de déclencher "l'instruction significative suivante dans cette méthode" après l'opération, vous devrez soit la bloquer, soit déclencher l'instruction significative dans le rappel.

+0

Si je bloque le thread d'interface utilisateur, le BackgroundWorker de la bibliothèque ne pourra pas déclencher l'événement RunWorkerCompleted, qui se produit sur le thread d'interface utilisateur. Ainsi, l'application sera accrochée. – AngryHacker

+0

Travailleur d'arrière-plan a été construit à l'origine avec l'idée qu'il pourrait être utilisé à partir du niveau de l'interface utilisateur pour déclencher des processus asynchrones qui seraient ensuite ramasser à l'événement terminé. –

+0

@AngryHacker: Sauf si vous débloquez avant, dans le fil d'arrière-plan. –