Je travaille sur une opération asynchrone qui doit appeler d'autres tâches asynchrones. J'essaie de garder les choses simples en utilisant BackgroundWorkers, avec pour résultat qu'un callback DoWork() de BackgroundWorker appelle une méthode qui crée un second BackgroundWorker, comme ça (sans vérification d'erreur et tout ce jazz pour la brièveté):BackgroundWorkers imbriqués: appels RunWorkerCompleted sur le mauvais thread?
class Class1
{
private BackgroundWorker _worker = null;
public void DoSomethingAsync()
{
_worker = new BackgroundWorker();
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.RunWorkerAsync();
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
Class2 foo = new Class2();
foo.DoSomethingElseAsync();
while(foo.IsBusy) Thread.Sleep(0); // try to wait for foo to finish.
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// do stuff
}
}
class Class2
{
private BackgroundWorker _worker = null;
Thread _originalThread = null;
public AsyncCompletedEventHandler DoSomethingCompleted;
public bool IsBusy { get { return _worker != null && _worker.IsBusy; } }
public void DoSomethingElseAsync()
{
_originalThread = Thread.CurrentThread;
_worker = new BackgroundWorker();
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.RunWorkerAsync();
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
// do stuff
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.Assert(Thread.CurrentThread == _originalThread); // fails
// Assuming the above Assert() were excluded, the following event would be raised on the wrong thread.
if (DoSomethingCompleted != null) DoSomethingCompleted(this, new AsyncCompletedEventArgs(e.Error, e.Cancelled, null));
}
}
Donc, le problème est, je m'attends à ce que Class2._Worker_RunWorkerCompleted() s'exécute sur le même thread sur lequel Class2.DoSomethingElseAsync() a été appelé. Cela n'arrive jamais - à la place, le rappel fonctionne sur un thread complètement nouveau.
Voici ma suspicion: _worker_DoWork() de Class1 ne revient jamais, ce qui signifie que le thread ne reviendrait jamais à un écouteur d'événement, même s'il existait (je soupçonne que non). D'un autre côté, si _worker_DoWork() revenait, BackgroundWorker de Class1 finirait automatiquement prématurément - il doit attendre que Class2 ait fini de fonctionner avant de pouvoir terminer son travail.
Cela conduit à deux questions:
- je soupçonne pas?
- Quelle est la meilleure façon d'imbriquer des opérations asynchrones comme celle-ci? Puis-je récupérer l'approche BackgroundWorker ou y a-t-il une autre technique plus appropriée?
Merci, cela m'a définitivement pointé dans la bonne direction. Il s'avère que je devrais implémenter le "modèle asynchrone basé sur l'événement" (http://msdn.microsoft.com/en-us/library/wewwczdw%28v=VS.90%29.aspx) pour gérer ce travail. Il utilise la classe AsyncOperation au lieu de BackgroundWorker, ce qui donne un certain contrôle sur le contexte de synchronisation auquel le message est destiné. –