Questo è probabilmente TL; DR per molti, ma, penso che confrontarsi await
con BackgroundWorker
è come confrontare mele e arance e i miei pensieri su questo seguono:
BackgroundWorker
è pensato per modellare una singola attività che vorresti eseguire in background, su un thread del pool di thread. async
/ await
è una sintassi per l'attesa asincrona delle operazioni asincrone. Tali operazioni possono o meno utilizzare un thread del pool di thread o addirittura utilizzare qualsiasi altro thread . Quindi, sono mele e arance.
Ad esempio, puoi fare qualcosa come il seguente con await
:
using (WebResponse response = await webReq.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
}
}
Ma probabilmente non lo modelleresti mai in un lavoratore in background, potresti fare qualcosa del genere in .NET 4.0 (prima di 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);
Notare la disgiunzione della disposizione rispetto alle due sintassi e come non è possibile utilizzare using
senza async
/ await
.
Ma non faresti qualcosa del genere con BackgroundWorker
. BackgroundWorker
è in genere per la modellazione di un'unica operazione di lunga durata che non si desidera influire sulla reattività dell'interfaccia utente. Per esempio:
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();
Non c'è davvero niente lì con cui puoi usare asincronizzazione / attesa, BackgroundWorker
è creare il thread per te.
Ora puoi usare TPL invece:
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);
Nel qual caso TaskScheduler
sta creando il thread per te (assumendo il valore predefinito TaskScheduler
) e potrebbe usare await
come segue:
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"
Secondo me, un confronto importante è se stai segnalando progressi o meno. Ad esempio, potresti avere BackgroundWorker like
questo:
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();
Ma non ti occuperesti di questo perché trascinerai il componente di lavoro in background nell'area di progettazione di un modulo - qualcosa che non puoi fare con async
/ await
e Task
... cioè che hai vinto ' t creare manualmente l'oggetto, impostare le proprietà e impostare i gestori di eventi. devi compilare solo nel corpo dei DoWork
, RunWorkerCompleted
e ProgressChanged
gestori di eventi.
Se lo "convertissi" in asincrono / attendi, faresti qualcosa del tipo:
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"
Senza la possibilità di trascinare un componente su una superficie di Designer, spetta al lettore decidere quale sia "migliore". Ma questo, per me, è il confronto tra await
e BackgroundWorker
, non se puoi aspettare metodi integrati come Stream.ReadAsync
. ad esempio se si utilizzava BackgroundWorker
come previsto, potrebbe essere difficile convertirlo in uso await
.
Altre riflessioni: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html