Async / await vs BackgroundWorker



в последние несколько дней я тестировал новые возможности .net 4.5 и c# 5.

Мне нравятся его новые функции async / await. Раньше я использовал BackgroundWorker для обработки более длительных процессов в фоновом режиме с адаптивным интерфейсом.

мой вопрос: после того, как эти хорошие новые функции, когда я должен использовать async / await и когда BackgroundWorker? Каковы общие сценарии для обоих?

232   4  

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:

статья MSDN " асинхронное программирование с асинхронностью и ожиданием (C# и Visual Basic)" говорит:

асинхронный подход к асинхронному программированию предпочтительнее к существующим подходам практически в каждом случае. В частности, это подход лучше, чем BackgroundWorkerдля операций с привязкой ввода-вывода потому что код проще и вам не нужно остерегаться гонки условия. В сочетании с задачей.Запуск, асинхронное программирование лучше чем BackgroundWorkerдля операций, связанных с ЦП потому, что асинхронность Программирование разделяет детали координации выполнения кода от работы задач.Беги переводы в threadpool

обновление

  • в ответ @eran-otzapкомментарий:
    "для операций с привязкой к IO, потому что код проще, и вам не нужно защищать от условий гонки" какая гонка условия могут возникнуть, не могли бы вы привести пример ? "

этот вопрос должен был быть поставлен как отдельный пост.

Википедия имеет хорошее объяснение гонки условиях. Необходимая часть его-многопоточность и из той же статьи MSDN асинхронное программирование с асинхронностью и ожиданием (C# и Visual Basic):

асинхронные методы предназначены для неблокирующих операций. Гостей ждут выражение в асинхронном методе не блокирует текущий поток, пока ожидаемая задача выполняется. Напротив, выражения, знаки остальных метода в качестве продолжения и возвращает управление вызывающему объекту асинхронный метод.

ключевые слова async и await не вызывают дополнительных потоков создан. Асинхронные методы не требуют многопоточности, потому что асинхронный метод не выполняется в собственном потоке. Метод выполняется на текущем контекст синхронизации и использование время в потоке только тогда, когда метод активен. Вы можете использовать Task.Бег для перемещения ЦП к фоновый поток, но фоновый поток не помогает с процессом это просто ожидание результатов, чтобы стать доступными.

асинхронный подход к асинхронному программированию предпочтительнее существующие подходы практически в каждом случае. В частности, такой подход лучше, чем BackgroundWorker для операций с привязкой IO, потому что код проще, а вы нет в условиях гонки. В сочетании с задачей.Запуск, асинхронное программирование лучше, чем BackgroundWorker для операций, связанных с ЦП, поскольку асинхронное программирование отделяет детали координации выполнения кода от работы эта задача.Запуск передачи в threadpool

то есть,"ключевые слова async и await не вызывают создания дополнительных потоков".

насколько я могу вспомнить свои собственные попытки, когда я изучал эту статью a год назад, если вы запустили и играли с образцом кода из той же статьи, вы можете столкнуться с ситуацией, когда его неасинхронные версии (вы можете попытаться преобразовать его в себя) блокируются на неопределенный срок!

кроме того, для конкретных примеров вы можете найти этот сайт. Вот некоторые примеры:

    Ничего не найдено.

Добавить ответ:
Отменить.