Dies ist wahrscheinlich TL; DR für viele, aber ich denke, das Vergleichen await
mit BackgroundWorker
ist wie das Vergleichen von Äpfeln und Orangen, und meine Gedanken dazu folgen:
BackgroundWorker
soll eine einzelne Aufgabe modellieren, die Sie im Hintergrund in einem Thread-Pool-Thread ausführen möchten. async
/ await
ist eine Syntax für das asynchrone Warten auf asynchrone Operationen. Diese Operationen können einen Thread-Pool-Thread oder sogar einen anderen Thread verwenden oder nicht . Sie sind also Äpfel und Orangen.
Sie können beispielsweise Folgendes tun mit await
:
using (WebResponse response = await webReq.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
}
}
Aber Sie würden wahrscheinlich nie modellieren, dass Sie in einem Hintergrund-Worker wahrscheinlich so etwas in .NET 4.0 (vorher await
) tun würden :
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);
Beachten Sie die Disjunktheit der Entsorgung im Vergleich zwischen den beiden Syntaxen und wie Sie using
ohne async
/ nicht verwenden können await
.
Aber so etwas würden Sie nicht machen BackgroundWorker
. BackgroundWorker
dient normalerweise zum Modellieren einer einzelnen Operation mit langer Laufzeit, die die Reaktionsfähigkeit der Benutzeroberfläche nicht beeinträchtigen soll. Beispielsweise:
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();
Es gibt wirklich nichts, mit dem Sie async / await verwenden können. Es BackgroundWorker
erstellt den Thread für Sie.
Jetzt können Sie stattdessen TPL verwenden:
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);
In diesem Fall TaskScheduler
erstellt der Thread den Thread für Sie (unter der Annahme der Standardeinstellung TaskScheduler
) und kann await
wie folgt verwendet werden:
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"
Meiner Meinung nach ist ein wichtiger Vergleich, ob Sie Fortschritte melden oder nicht. Zum Beispiel könnten Sie Folgendes haben BackgroundWorker like
:
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();
Aber Sie würden sich nicht mit etwas davon befassen, weil Sie die Hintergrund-Worker-Komponente auf die Entwurfsoberfläche eines Formulars ziehen und dort ablegen würden - etwas, das Sie mit async
/ await
und Task
... nicht tun können, dh Sie haben gewonnen. ' t Erstellen Sie das Objekt manuell, legen Sie die Eigenschaften und die Ereignishandler fest. Sie würden nur in dem Körper der füllen DoWork
, RunWorkerCompleted
und ProgressChanged
Event - Handler.
Wenn Sie das in async / await "konvertieren" würden, würden Sie etwas tun wie:
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"
Ohne die Möglichkeit, eine Komponente auf eine Designer-Oberfläche zu ziehen, muss der Leser wirklich entscheiden, welche "besser" ist. Aber das ist für mich der Vergleich zwischen await
und BackgroundWorker
nicht, ob Sie auf eingebaute Methoden wie warten können Stream.ReadAsync
. Wenn Sie beispielsweise BackgroundWorker
wie vorgesehen verwenden, kann es schwierig sein, die Konvertierung in die Verwendung zu konvertieren await
.
Andere Gedanken: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html