Async / await vs BackgroundWorker
в последние несколько дней я тестировал новые возможности .net 4.5 и c# 5.
Мне нравятся его новые функции async / await. Раньше я использовал BackgroundWorker для обработки более длительных процессов в фоновом режиме с адаптивным интерфейсом.
мой вопрос: после того, как эти хорошие новые функции, когда я должен использовать async / await и когда BackgroundWorker? Каковы общие сценарии для обоих?
4 ответов:
async / await предназначен для замены конструкций, таких как
BackgroundWorker. Хотя ты конечно можете используйте его, если вы хотите, вы должны быть в состоянии использовать async/await, наряду с несколькими другими инструментами TPL, чтобы обрабатывать все, что там.поскольку оба работают, это сводится к личным предпочтениям относительно того, что вы используете, когда. Что быстрее для вы? Что проще для вы понять?
это, вероятно, 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/await,
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... т. е. вы не будете вручную создавать объект, устанавливать свойства и устанавливать обработчики событий. ты только заполните в телеDoWork,RunWorkerCompletedиProgressChangedобработчики событий.если вы "преобразовали" это в async / await, вы бы сделали что-то вроде:
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
Это хорошее введение:http://msdn.microsoft.com/en-us/library/hh191443.aspx Раздел Темы-это именно то, что вы ищете:
асинхронные методы предназначены для неблокирующих операций. Выражение await в async-метод не блокирует текущий поток на время выполнения ожидаемой задачи. Вместо этого выражение подписывает остальную часть метода как продолжение и возвращает управление вызывающему асинхронному методу.
ключевые слова async и await не вызывают создания дополнительных потоков. Асинхронные методы не требуют многопоточности, поскольку асинхронный метод не выполняется в собственном потоке. Метод выполняется в текущем контексте синхронизации и использует время в потоке только тогда, когда метод активен. Вы можете использовать Task.Запустите, чтобы переместить работу с привязкой к ЦП в фоновый поток, но фоновый поток не помогает процессу, который просто ждет результатов, чтобы стать доступным.
в асинхронный подход к асинхронному программированию предпочтительнее существующих подходов практически в каждом случае. В частности, этот подход лучше, чем BackgroundWorker для операций с привязкой к IO, потому что код проще, и вам не нужно защищаться от условий гонки. В сочетании с задачей.Запуск, асинхронное программирование лучше, чем BackgroundWorker для операций с привязкой к ЦП, потому что асинхронное программирование отделяет детали координации выполнения кода от работы этой задачи.Выполнения переводов в бассейн с нитями.
BackgroundWorker явно помечен как устаревший в .NET 4.5:
- в книге Джозеф Албахари, Бен Албахари "C# 5.0 в двух словах: окончательная ссылка"
- ответ Стивена Клири на мой вопрос "разве не .NET 4.0 TPL сделал асинхронные шаблоны APM, EAP и BackgroundWorker устаревшими?"
статья MSDN " асинхронное программирование с асинхронностью и ожиданием (C# и Visual Basic)" говорит:
асинхронный подход к асинхронному программированию предпочтительнее к существующим подходам практически в каждом случае. В частности, это подход лучше, чем BackgroundWorkerдля операций с привязкой ввода-вывода потому что код проще и вам не нужно остерегаться гонки условия. В сочетании с задачей.Запуск, асинхронное программирование лучше чем BackgroundWorkerдля операций, связанных с ЦП потому, что асинхронность Программирование разделяет детали координации выполнения кода от работы задач.Беги переводы в threadpool
обновление
- в ответ @eran-otzapкомментарий:
"для операций с привязкой к IO, потому что код проще, и вам не нужно защищать от условий гонки" какая гонка условия могут возникнуть, не могли бы вы привести пример ? "этот вопрос должен был быть поставлен как отдельный пост.
Википедия имеет хорошее объяснение гонки условиях. Необходимая часть его-многопоточность и из той же статьи MSDN асинхронное программирование с асинхронностью и ожиданием (C# и Visual Basic):
асинхронные методы предназначены для неблокирующих операций. Гостей ждут выражение в асинхронном методе не блокирует текущий поток, пока ожидаемая задача выполняется. Напротив, выражения, знаки остальных метода в качестве продолжения и возвращает управление вызывающему объекту асинхронный метод.
ключевые слова async и await не вызывают дополнительных потоков создан. Асинхронные методы не требуют многопоточности, потому что асинхронный метод не выполняется в собственном потоке. Метод выполняется на текущем контекст синхронизации и использование время в потоке только тогда, когда метод активен. Вы можете использовать Task.Бег для перемещения ЦП к фоновый поток, но фоновый поток не помогает с процессом это просто ожидание результатов, чтобы стать доступными.
асинхронный подход к асинхронному программированию предпочтительнее существующие подходы практически в каждом случае. В частности, такой подход лучше, чем BackgroundWorker для операций с привязкой IO, потому что код проще, а вы нет в условиях гонки. В сочетании с задачей.Запуск, асинхронное программирование лучше, чем BackgroundWorker для операций, связанных с ЦП, поскольку асинхронное программирование отделяет детали координации выполнения кода от работы эта задача.Запуск передачи в threadpool
то есть,"ключевые слова async и await не вызывают создания дополнительных потоков".
насколько я могу вспомнить свои собственные попытки, когда я изучал эту статью a год назад, если вы запустили и играли с образцом кода из той же статьи, вы можете столкнуться с ситуацией, когда его неасинхронные версии (вы можете попытаться преобразовать его в себя) блокируются на неопределенный срок!
кроме того, для конкретных примеров вы можете найти этот сайт. Вот некоторые примеры:
Comments