Це, мабуть, TL; DR для багатьох, але, я думаю, порівняння await
з BackgroundWorker
подібним до порівняння яблук і апельсинів, і мої думки з цього приводу:
BackgroundWorker
призначений для моделювання єдиного завдання, яке ви хочете виконати у фоновому режимі, на потоці пулу потоків. async
/ await
- синтаксис для асинхронно очікуваних асинхронних операцій. Ці операції можуть або не можуть використовувати нитку пулу потоків або навіть використовувати будь-яку іншу нитку . Отже, це яблука та апельсини.
Наприклад, ви можете зробити щось на зразок наступного await
:
using (WebResponse response = await webReq.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
}
}
Але ви, ймовірно, ніколи не моделюватимете, що це фоновий працівник, ви, ймовірно, зробите щось подібне в .NET 4.0 (до 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);
Зауважте нерозбірливість утилізації порівняно між двома синтаксисами та те, як ви не можете користуватися using
без async
/ await
.
Але, ти б нічого подібного не робив BackgroundWorker
. BackgroundWorker
зазвичай для моделювання однієї тривалої операції, на яку не хочеться впливати на чуйність інтерфейсу. Наприклад:
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();
Там насправді немає нічого, з чим можна використовувати async / ждати, BackgroundWorker
- це створює нитку для вас.
Тепер ви можете використовувати TPL замість:
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);
У такому випадку, TaskScheduler
це створює нитку для вас (при умові за замовчуванням TaskScheduler
), і може використовувати await
наступне:
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"
На мою думку, головне порівняння - це ви, чи повідомляєте ви про прогрес чи ні. Наприклад, у вас може бути 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();
Але ви не мали б справу з цим, тому що ви перетягуєте компонент фонового робочого на дизайнерську поверхню форми - те, що ви не можете зробити з async
/ await
і Task
... тобто ви виграли ' t вручну створити об'єкт, встановити властивості та встановити обробники подій. Ви б заповнити тільки в тілі DoWork
, RunWorkerCompleted
і ProgressChanged
обробники подій.
Якщо ви "перетворили" це на асинхронізацію / очікування, ви зробите щось на кшталт:
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"
Без можливості перетягувати компонент на поверхню дизайнера, читач дійсно повинен вирішити, який "кращий". Але це, на мій погляд, порівняння між, await
а BackgroundWorker
не тим, чи можна чекати вбудованих методів на кшталт Stream.ReadAsync
. Наприклад, якщо ви використовували BackgroundWorker
за призначенням, це може бути важко перетворити на використання await
.
Інші думки: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html