Ceci est probablement trop long à lire pour beaucoup d'entre vous, mais je pense que comparer await
avec BackgroundWorker
revient à comparer des pommes et des oranges et mes réflexions à ce sujet suivent :
BackgroundWorker
est destiné à modéliser une tâche unique que vous souhaitez exécuter en arrière-plan, sur un thread du pool de threads. async
/await
est une syntaxe pour attendre de manière asynchrone sur des opérations asynchrones. Ces opérations peuvent ou non utiliser un thread pool ou même utiliser n'importe quel autre thread. Ainsi, ils sont comme des pommes et des oranges.
Par exemple, vous pouvez faire quelque chose comme ce qui suit avec await
:
using (WebResponse response = await webReq.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
}
}
Mais vous ne modéliseriez probablement jamais cela dans un worker en arrière-plan, vous feriez probablement quelque chose comme ceci en .NET 4.0 (avant await
) :
webReq.BeginGetResponse(ar =>
{
WebResponse response = webReq.EndGetResponse(ar);
Stream responseStream = response.GetResponseStream();
responseStream.BeginRead(buffer, 0, buffer.Length, ar2 =>
{
int bytesRead = responseStream.EndRead(ar2);
responseStream.Dispose();
((IDisposable) response).Dispose();
}, null);
}, null);
Remarquez la différence de gestion de la libération entre les deux syntaxes et le fait que vous ne pouvez pas utiliser using
sans async
/await
.
Mais, vous ne feriez pas quelque chose comme ça avec BackgroundWorker
. BackgroundWorker
est généralement destiné à modéliser une seule opération de longue durée que vous ne voulez pas impacter la réactivité de l'interface utilisateur. Par exemple :
worker.DoWork += (sender, e) =>
{
int i = 0;
// simuler une opération de longue durée
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
};
worker.RunWorkerCompleted += (sender, eventArgs) =>
{
// À FAIRE : quelque chose sur le thread de l'interface utilisateur, comme
// mettre à jour le statut ou afficher le "résultat"
};
worker.RunWorkerAsync();
Il n'y a vraiment rien là où vous pourriez utiliser async/await, BackgroundWorker
crée le thread pour vous.
Maintenant, vous pourriez utiliser le TPL à la place :
var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
int i = 0;
// simuler une opération de longue durée
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
}).ContinueWith(t=>
{
// À FAIRE : quelque chose sur le thread de l'interface utilisateur, comme
// mettre à jour le statut ou afficher le "résultat"
}, synchronizationContext);
Dans ce cas, le TaskScheduler
crée le thread pour vous (en supposant le TaskScheduler
par défaut), et pourrait utiliser await
comme suit :
await Task.Factory.StartNew(() =>
{
int i = 0;
// simuler une opération de longue durée
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
});
// À FAIRE : quelque chose sur le thread de l'interface utilisateur, comme
// mettre à jour le statut ou afficher le "résultat"
À mon avis, une comparaison importante concerne la gestion de la progression ou non. Par exemple, vous pourriez avoir un BackgroundWorker
comme ceci :
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
{
// À FAIRE : quelque chose avec la progression, comme mettre à jour la barre de progression
};
worker.DoWork += (sender, e) =>
{
int i = 0;
// simuler une opération de longue durée
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
{
if ((sw.Elapsed.TotalMilliseconds%100) == 0)
((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds));
++i;
}
};
worker.RunWorkerCompleted += (sender, eventArgs) =>
{
// Faire quelque chose sur le thread de l'interface utilisateur, comme
// mettre à jour le statut ou afficher le "résultat"
};
worker.RunWorkerAsync();
Mais vous ne traiteriez pas certaines de ces choses parce que vous glisseriez-déposeriez le composant worker en arrière-plan sur la surface de design d'un formulaire - quelque chose que vous ne pouvez pas faire avec async
/await
et Task
... c'est-à-dire que vous ne créez pas manuellement l'objet, ne définissez pas les propriétés et ne définissez pas les gestionnaires d'événements. vous rempliriez seulement le corps des gestionnaires d'événements DoWork
, RunWorkerCompleted
et ProgressChanged
.
Si vous "convertissiez" cela en async/await, vous feriez quelque chose comme :
IProgress progress = new Progress();
progress.ProgressChanged += ( s, e ) =>
{
// À FAIRE : quelque chose avec e.ProgressPercentage
// comme mettre à jour la barre de progression
};
await Task.Factory.StartNew(() =>
{
int i = 0;
// simuler une opération de longue durée
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
{
if ((sw.Elapsed.TotalMilliseconds%100) == 0)
{
progress.Report((int) (1000 / sw.ElapsedMilliseconds))
}
++i;
}
});
// À FAIRE : quelque chose sur le thread de l'interface utilisateur, comme
// mettre à jour le statut ou afficher le "résultat"
Sans la possibilité de glisser un composant sur une surface de design, c'est vraiment à vous de décider lequel est "meilleur". Mais, pour moi, c'est là la comparaison entre await
et BackgroundWorker
, non pas si vous pouvez attendre des méthodes intégrées comme Stream.ReadAsync
. par exemple, si vous utilisez BackgroundWorker
comme prévu, il pourrait être difficile de le convertir pour utiliser await
.
Autres réflexions : http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html
0 votes
Voir également stackoverflow.com/questions/3513432/… et stackoverflow.com/questions/4054263/…
0 votes
Les deux sont bons, mais si vous travaillez avec du code plus ancien qui n'a pas été migré vers une version ultérieure .net ; BackgroundWorker fonctionne sur les deux.