C'est probablement TL; DR pour beaucoup, mais je pense que comparer await
avec, BackgroundWorker
c'est comme comparer des pommes et des oranges et mes pensées à ce sujet sont les suivantes:
BackgroundWorker
est destiné à modéliser une tâche unique que vous souhaitez effectuer en arrière-plan, sur un thread de pool de threads. async
/ await
est une syntaxe pour l'attente asynchrone sur les opérations asynchrones. Ces opérations peuvent ou non utiliser un thread de pool de threads ou même utiliser n'importe quel autre thread . Donc, ce sont 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 travailleur d'arrière-plan, vous feriez probablement quelque chose comme ça dans .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 disjonction de l'élimination par rapport aux deux syntaxes et comment 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 dont vous ne souhaitez pas affecter la réactivité de l'interface utilisateur. Par exemple:
worker.DoWork += (sender, e) =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
};
worker.RunWorkerCompleted += (sender, eventArgs) =>
{
// TODO: do something on the UI thread, like
// update status or display "result"
};
worker.RunWorkerAsync();
Il n'y a vraiment rien avec lequel vous pouvez utiliser async / await, qui BackgroundWorker
crée le thread pour vous.
Maintenant, vous pouvez utiliser TPL à la place:
var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
}).ContinueWith(t=>
{
// TODO: do something on the UI thread, like
// update status or display "result"
}, synchronizationContext);
Dans ce cas, le TaskScheduler
crée le thread pour vous (en supposant la valeur par défaut TaskScheduler
), et pourrait utiliser await
comme suit:
await Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
});
// TODO: do something on the UI thread, like
// update status or display "result"
À mon avis, une comparaison majeure est de savoir si vous faites état de progrès ou non. Par exemple, vous pourriez avoir un BackgroundWorker like
ceci:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
{
// TODO: something with progress, like update progress bar
};
worker.DoWork += (sender, e) =>
{
int i = 0;
// simulate lengthy operation
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) =>
{
// do something on the UI thread, like
// update status or display "result"
};
worker.RunWorkerAsync();
Mais, vous ne traiteriez pas une partie de cela parce que vous feriez glisser-déposer le composant de travail d'arrière-plan sur l'aire de conception d'un formulaire - quelque chose que vous ne pouvez pas faire avec async
/ await
et Task
... c'est-à-dire que vous avez gagné ' t créer manuellement l'objet, définir les propriétés et définir les gestionnaires d'événements. vous souhaitez remplir uniquement le corps des DoWork
, RunWorkerCompleted
et des ProgressChanged
gestionnaires d'événements.
Si vous "convertissez" cela en async / await, vous feriez quelque chose comme:
IProgress<int> progress = new Progress<int>();
progress.ProgressChanged += ( s, e ) =>
{
// TODO: do something with e.ProgressPercentage
// like update progress bar
};
await Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
{
if ((sw.Elapsed.TotalMilliseconds%100) == 0)
{
progress.Report((int) (1000 / sw.ElapsedMilliseconds))
}
++i;
}
});
// TODO: do something on the UI thread, like
// update status or display "result"
Sans la possibilité de faire glisser un composant sur une surface Designer, c'est vraiment au lecteur de décider lequel est «meilleur». Mais, pour moi, c'est la comparaison entre await
et BackgroundWorker
, pas si vous pouvez attendre des méthodes intégrées comme Stream.ReadAsync
. par exemple, si vous utilisiez BackgroundWorker
comme prévu, il pourrait être difficile de le convertir await
.
Autres réflexions: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html