비동기 / 대기 대 BackgroundWorker


162

지난 며칠 동안 .net 4.5 및 c # 5의 새로운 기능을 테스트했습니다.

새로운 비동기 / 대기 기능이 마음에 듭니다. 이전 에는 반응 형 UI로 백그라운드에서 더 긴 프로세스를 처리하기 위해 BackgroundWorker 를 사용했습니다 .

내 질문은 :이 멋진 새로운 기능을 가지고 나면 언제 async / await를 사용해야하고 BackgroundWorker를 사용해야 합니까? 둘 다에 대한 일반적인 시나리오는 무엇입니까?



둘 다 좋지만 최신 .net 버전으로 마이그레이션되지 않은 이전 코드로 작업하는 경우; BackgroundWorker는 두 가지 모두에서 작동합니다.
dcarl661

답변:


74

async / await는와 같은 구문을 대체하도록 설계되었습니다 BackgroundWorker. 원하는 경우 확실히 사용할 수 있지만 다른 TPL 도구와 함께 async / await를 사용하여 외부에있는 모든 것을 처리 할 수 ​​있어야합니다.

두 가지 모두 작동하기 때문에 언제 사용하는지에 대한 개인적인 취향에 달려 있습니다. 대한 빠른 무엇 당신 ? 무엇을 위해 쉽게 당신이 이해하기?


17
감사. 나를 위해 async / await는 훨씬 더 명확하고 '자연스러워 보입니다. BakcgoundWorker는 제 생각에 코드를 '잡음'으로 만듭니다.
Tom

12
@Tom 음, Microsoft가이를 구현하는 데 많은 시간과 노력을 들인 이유 입니다. 그것이 더 나아지지 않았다면 그들은 귀찮게하지 않았을 것입니다
Servy

5
예. 새로운 기능은 이전 BackgroundWorker가 완전히 열등하고 쓸모없는 것처럼 보입니다. 차이점은 그 극적인 것입니다.
usr

16
백그라운드 작업에 대한 다양한 접근 방식을 비교 하는 내 블로그 에는 꽤 좋은 설명이 있습니다. 그 주 async/ await비동기 프로그래밍 할 수 없는 스레드 풀 스레드.
Stephen Cleary

8
이 답변을 다운 보트하면 오해의 소지가 있습니다. 비동기 / 대기는 백그라운드 워커를 대체하도록 설계되지 않았습니다.
Quango

206

이것은 많은 사람들에게 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없이 사용 하지 않는 방법에 주목하십시오 .asyncawait

그러나으로 그런 일을하지 않을 것입니다 BackgroundWorker. BackgroundWorkerUI 응답성에 영향을 미치지 않으려는 장기 실행 작업을 모델링하는 데 주로 사용됩니다. 예를 들면 다음과 같습니다.

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/ awaitTask'즉, 당신이 원 ... t 객체를 수동으로 생성하고 속성을 설정하고 이벤트 핸들러를 설정하십시오. 당신은 단지의 몸을 기입 것 DoWork, RunWorkerCompletedProgressChanged이벤트 핸들러.

이를 "비동기 / 대기"로 변환 한 경우 다음과 같은 작업을 수행합니다.

     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"

구성 요소를 Designer 화면으로 끌어 올 수 없으면 "더 나은"구성 요소를 결정하는 것은 독자의 몫입니다. 그러나, 나에게, 와 같은 내장 메소드를 기다릴 수 있는지 아닌지 await와 의 비교입니다.BackgroundWorkerStream.ReadAsync 입니다. 예를 들어 BackgroundWorker의도 한대로 사용 하는 경우 사용 으로 변환하기 어려울 수 있습니다 await.

다른 생각 : http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html


2
async / await에 존재하는 한 가지 결함은 한 번에 여러 개의 비동기 작업을 시작할 수 있다는 것입니다. await는 다음 작업을 시작하기 전에 각 작업이 완료 될 때까지 기다리는 것입니다. await 키워드를 생략하면 메소드가 동기식으로 실행되므로 원하는 것이 아닙니다. async / await가 "이 5 개의 작업을 시작하고 각 작업이 특정 순서로 수행되지 않으면 다시 전화하십시오"와 같은 문제를 해결할 수 있다고 생각하지 않습니다.
Trevor Elliott

4
@Moozhe. 사실이 아닙니다 var t1 = webReq.GetResponseAsync(); var t2 = webReq2.GetResponseAsync(); await t1; await t2;. 두 개의 병렬 작업을 기다리는 것입니다. Await는 비동기 적이지만 순차적 작업 인 IMO에 훨씬 좋습니다.
Peter Ritchie

2
@Moozhe 예, 그렇게하면 특정 순서가 유지됩니다. 이것이 중요한 것은 순차적으로 보이는 코드에서 비동기 성을 얻는 것입니다. 물론 await Task.WhenAny(t1, t2)두 작업 중 하나가 먼저 완료되었을 때 무언가를 수행 하는 데 사용할 수 있습니다. 다른 작업도 완료하도록 루프를 원할 것입니다. 일반적으로 특정 작업이 완료되는 시기를 알고 싶어서 순차적을 작성하게됩니다 await.
Peter Ritchie


5
정직하게도 BackgroundWorker는 IO 바인딩 작업에 적합 하지 않았습니다 .
피터 리치

21

이것은 좋은 소개입니다 : http://msdn.microsoft.com/en-us/library/hh191443.aspx 스레드 섹션은 당신이 찾고있는 것입니다.

비동기 메서드는 비 차단 작업입니다. 비동기 메서드의 대기 식은 대기중인 작업이 실행되는 동안 현재 스레드를 차단하지 않습니다. 대신, 표현식은 나머지 메소드를 연속으로 등록하고 비동기 메소드의 호출자에게 제어를 리턴합니다.

async 및 await 키워드는 추가 스레드를 생성하지 않습니다. 비동기 메서드는 자체 스레드에서 실행되지 않으므로 비동기 메서드에는 멀티 스레딩이 필요하지 않습니다. 메소드는 현재 동기화 컨텍스트에서 실행되며 메소드가 활성화 된 경우에만 스레드에서 시간을 사용합니다. Task.Run을 사용하여 CPU 바운드 작업을 백그라운드 스레드로 이동할 수 있지만 백그라운드 스레드는 결과를 사용할 수있을 때까지 기다리는 프로세스에는 도움이되지 않습니다.

비동기 프로그래밍에 대한 비동기 기반 접근 방식은 거의 모든 경우에 기존 접근 방식보다 선호됩니다. 특히,이 방법은 코드가 더 간단하고 경쟁 조건을 막을 필요가 없기 때문에 IO 바운드 작업의 경우 BackgroundWorker보다 좋습니다. 비동기 프로그래밍은 Task.Run이 스레드 풀로 전송하는 작업과 코드 실행에 대한 조정 세부 사항을 분리하기 때문에 Task.Run과 함께 CPU에 바인딩 된 작업의 경우 BackgroundWorker보다 비동기 프로그래밍이 더 좋습니다.


"코드가 단순하고 경쟁 조건으로부터 보호 할 필요가 없기 때문에 IO 바운드 작업의 경우"어떤 경쟁 조건이 발생할 수 있습니까? 예를 들어 주시겠습니까?
eran otzap

8

BackgroundWorker 는 .NET 4.5에서 사용되지 않는 것으로 명시 적으로 표시됩니다.

MSDN 기사 "Async 및 Await를 사용한 비동기 프로그래밍 (C # 및 Visual Basic)" 은 다음과 같이 알려줍니다.

비동기 프로그래밍에 대한 비동기 기반 접근 방식은 거의 모든 경우에 기존 접근 방식보다 선호됩니다 . 특히,이 방법은 코드가 더 간단하고 경쟁 조건을 막을 필요가 없기 때문에 IO 바운드 작업의 경우 BackgroundWorker 보다 낫습니다 . 비동기 프로그래밍은 Task.Run 이 스레드 풀로 전송 하는 작업과 코드 실행에 대한 조정 세부 사항을 분리하기 때문에 Task.Run과 함께 CPU 기반 작업의 경우 비동기 프로그래밍이 BackgroundWorker 보다 낫습니다.

최신 정보

  • 에 대한 응답으로 @, 둘다-otzap 의 '코멘트 :
    "IO-바운드 작업을위한 코드는 간단하고 당신은 경쟁 조건을 방지 할 필요가 없기 때문에"당신이 예를 줄 수, 경쟁 조건이 발생 시킬수 있습니다 무엇? "

이 질문은 별도의 게시물로 작성해야합니다.

Wikipedia에는 경주 조건에 대한 좋은 설명이 있습니다. 그중 필요한 부분은 멀티 스레딩이며 동일한 MSDN 기사 Async and Await를 사용한 비동기 프로그래밍 (C # 및 Visual Basic)입니다 .

비동기 메서드는 비 차단 작업입니다. 비동기 메서드의 대기 식은 대기중인 작업이 실행되는 동안 현재 스레드를 차단하지 않습니다. 대신, 표현식은 나머지 메소드를 연속으로 등록하고 비동기 메소드의 호출자에게 제어를 리턴합니다.

async 및 await 키워드는 추가 스레드를 생성하지 않습니다. 비동기 메서드는 자체 스레드에서 실행되지 않으므로 비동기 메서드에는 멀티 스레딩이 필요하지 않습니다. 메소드는 현재 동기화 컨텍스트에서 실행되며 메소드가 활성화 된 경우에만 스레드에서 시간을 사용합니다. Task.Run을 사용하여 CPU 바운드 작업을 백그라운드 스레드로 이동할 수 있지만 백그라운드 스레드는 결과를 사용할 수있을 때까지 기다리는 프로세스에는 도움이되지 않습니다.

비동기 프로그래밍에 대한 비동기 기반 접근 방식은 거의 모든 경우에 기존 접근 방식보다 선호됩니다. 특히,이 방법은 코드가 더 간단하고 경쟁 조건을 막을 필요가 없기 때문에 IO 바운드 작업의 경우 BackgroundWorker보다 좋습니다. 비동기 프로그래밍은 Task.Run이 스레드 풀로 전송하는 작업과 코드 실행에 대한 조정 세부 사항을 분리하기 때문에 Task.Run과 함께 CPU 기반 작업의 경우 비동기 프로그래밍이 BackgroundWorker보다 낫습니다.

즉, "async 및 await 키워드는 추가 스레드를 만들지 않습니다".

1 년 전에이 기사를 공부할 때 내 시도를 기억할 수있는 한, 같은 기사의 코드 샘플을 실행하여 재생 한 경우 비동기 버전의 상황에 부딪 칠 수 있습니다 (변환을 시도 할 수 있음) 스스로에게) 무한정 차단!

또한 구체적인 예를 보려면이 사이트를 검색하십시오. 다음은 몇 가지 예입니다.


30
BackgrondWorker입니다 하지 명시 적으로 .NET 4.5에서 사용되지 않는로 표시. MSDN 기사에서는 IO 바인딩 작업이 비동기 메서드에서 더 낫다고 말합니다. BackgroundWorker를 사용한다고해서 비동기 메서드를 사용할 수 없다는 의미는 아닙니다.
피터 리치

@ PeterRitchie, 나는 내 대답을 수정했다. 저에게있어 "기존의 접근 방식은 쓸모가 없습니다"는 "비동기 프로그래밍에 대한 비동기 기반 접근 방식은 거의 모든 경우에 기존의 접근 방식보다 선호됩니다"와 동의어입니다.
Gennady Vanin Геннадий Ванин

7
해당 MSDN 페이지에 문제가 있습니다. 우선, BGW와는 "작업"보다 "조정"을하지 않아도됩니다. 그렇습니다. BGW는 직접 IO 작업을 수행하도록 의도 된 적이 없었습니다. 항상 BGW보다 IO를 수행하는 것이 더 좋은 방법이었습니다. 다른 답변은 BGW가 작업보다 더 복잡하지 않다는 것을 보여줍니다. BGW를 올바르게 사용하면 경쟁 조건이 없습니다.
피터 리치

"코드가 단순하고 경쟁 조건으로부터 보호 할 필요가 없기 때문에 IO 바운드 작업의 경우"어떤 경쟁 조건이 발생할 수 있습니까? 예를 들어 주시겠습니까?
eran otzap

11
이 답변은 잘못되었습니다. 비동기 프로그래밍은 사소한 프로그램에서 교착 상태를 너무 쉽게 트리거 할 수 있습니다. 이에 비해 BackgroundWorker는 단순하고 견고합니다.
ZunTzu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.