c# - 프로그래밍 - 유니티 async await




그것은 아니 었 NET 4.0 TPL은 APM, EAP 및 BackgroundWorker 비동기 패턴을 쓸모 없게 만들었습니까? (2)

2 가지 C # WPF 앱 프로젝트가 있습니다.

  • .NET 4.0 기반으로 .NET 4.5로 마이그레이션 할 수 없음
  • .NET 4.5 기반으로 마이그레이션 할 수있는 .NET 4.0 기반

그들 모두는 사용자가 취소하고 다시 시작할 수있는 2-10 개의 장기 실행 프로세스를 생성해야합니다.

나는 최고의 디자인 관행을 따르는 데 관심이있다. 우선, BackgroundWorker 사용법에 대한 명확성은 관심이 있지만, 제 질문은 다른 비동기 패턴에 대해 유효해야합니다.

나는 (상반되는) 동시적인 관점을 본다.

비동기 패턴 :

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

  • B) 그들 (또는 적어도 BackgroundWorker )은 .NET 4.5에서 폐기되지 않았습니다.

나는 아직도 의심 스럽다.

  1. NET 4.5에서 이러한 패턴 (우선, BGW)이 폐기 되었습니까?
  2. .NET 4.5에서 쓸모가 없으면 .NET 4.0에서 사용되지 않는 이유는 무엇입니까?

    2A) .NET 4.5의 새로운 기능이 여전히 .NET 4.0에서 쉽게 구현 / 재현 가능하다는 것을 잘못 이해합니까?


.NET 4.5에서는 이러한 패턴 (특히 APM, EAP 및 BGW)이 더 이상 사용되지 않습니다. asyncTask.Run 의 조합은 모든면에서 BGW보다 우수합니다. 사실, 방금 BGW를 Task.Run 과 비교하고 모든 상황에서 어떻게 더 성가신 지 보여주는 일련의 블로그를 시작했습니다. 약간 더 성가신 상황이 있지만, 훨씬 더 성가신 다른 상황이 있습니다.

이제는 .NET 4.0에서 쓸모 없는지 여부가 완전히 다른 질문입니다. 다른 게시물에서는 VS2010 을 사용하는 .NET 4.0 용으로 개발 중이므로 Microsoft.Bcl.Async 백 포트는 옵션이 아닙니다. 이 경우 APM이나 EAP 모두 구식 IMO로 간주 될 수 없습니다. 이 플랫폼에서는 BGW에 대한 대안으로 Task.Factory.StartNew 를 고려할 수 있지만 진행보고 및 진행 및 완료 이벤트의 자동 스레드 마샬링에 대해서는 BGW에 몇 가지 장점이 있습니다.

업데이트 : 저는 백그라운드 작업의 다양한 구현에 대해 논의 하는 오래된 블로그 게시물을 최근에 업데이트했습니다. 그 게시물에서 "Tasks (Async Methods)"에 대해 Task.Run , 모든 .NET 4.5 async 지원, Task.Run 등으로 Task 를 사용한다는 의미입니다. "Tasks (Task Parallel Library)"섹션은 Task 를 평가 중입니다. .NET 4.0에 존재했다.


나는 일반적으로 Task 추천하거나 .NET 4.5를 사용하고 있다면 await . 하지만 Task & BGW는 두 가지 시나리오를 가지고 있습니다. 작업은 연속적으로 연결될 수있는 일반적인 짧은 비동기 작업에 유용하며 암시 적으로 UI 스레드로 마샬링되는 작업을 수행하는 것이 좋습니다. BGW는 UI의 응답성에 영향을 미치지 않아야하는 하나의 긴 작업에 유용합니다. BGW를 디자인 화면으로 끌어서 놓고 두 번 클릭하여 이벤트 처리기를 만들 수 있습니다. 다른 스레드로 마샬링하지 않으려면 LongRunning 또는 ConfigureAwait 를 처리 할 필요가 없습니다. 많은 사람들이 IProgress<T> 보다 BGW 진행을 더 쉽게 찾습니다.

다음은 "긴 작업"시나리오에서 두 가지를 모두 사용하는 몇 가지 예입니다.

질문에 .NET 4.0이 구체적으로 언급 되었기 때문에 다음은 UI를 진행하면서 긴 Task 을 수행하는 Task 를 사용하는 간단한 코드입니다.

startButton.Enabled = false;
var task = Task.Factory.
                StartNew(() =>
                    {
                        foreach (var x in Enumerable.Range(1, 10))
                        {
                            var progress = x*10;
                            Thread.Sleep(500); // fake work
                            BeginInvoke((Action) delegate { 
                               progressBar1.Value = progress; 
                            });
                        }
                    }, TaskCreationOptions.LongRunning)
                .ContinueWith(t =>
                    {
                        startButton.Enabled = true;
                        progressBar1.Value = 0;
                    });

BackgroundWorker 와 비슷한 코드는 다음과 같습니다.

startButton.Enabled = false;
BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true };
bgw.ProgressChanged += (sender, args) => 
    { progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted += (sender, args) =>
{
    startButton.Enabled = true;
    progressBar1.Value = 0;
};
bgw.DoWork += (sender, args) =>
{
    foreach (var x in Enumerable.Range(1, 10))
    {
        Thread.Sleep(500);
        ((BackgroundWorker)sender).ReportProgress(x * 10);
    }
};
bgw.RunWorkerAsync();

이제 .NET 4.5를 사용하는 경우 Task BeginInvoke 호출 대신 Progress<T> 를 사용할 수 있습니다. 그리고 4.5 이후로, await 사용하는 것이 더 읽기 쉬울 것입니다 :

startButton.Enabled = false;
var pr = new Progress<int>();
pr.ProgressChanged += (o, i) => progressBar1.Value = i;
await Task.Factory.
            StartNew(() =>
                        {
                            foreach (var x in Enumerable.Range(1, 10))
                            {
                                Thread.Sleep(500); // fake work
                                ((IProgress<int>) pr).Report(x*10);
                            }
                        }, TaskCreationOptions.LongRunning);
startButton.Enabled = true;
progressBar1.Value = 0;

Progress<T> 사용하면 BackgroundWorker 가 특정 UI 프레임 워크에서 분리를 수행하는 것과 거의 같은 방식으로 코드가 특정 UI 프레임 워크 (예 : BeginInvoke )에 연결되지 않음을 의미합니다. 만약 당신이 신경 쓰지 않는다면, 당신은 Progress<T> 를 사용하는 추가 된 복잡성을 소개 할 필요가 없습니다.

LongRunning 관해서는 Stephen Toub이 말했습니다 : "일반적으로 성능 테스트를 통해 LongRunning을 사용하면 다른 작업을 처리하는 데 오랜 시간이 걸렸습니다." 추가 분석 또는 LongRunning 매개 변수를 항상 추가하는 "복잡성"이 있습니다. LongRunning을 사용하지 않으면 장기 실행 작업에 사용되는 스레드 풀 스레드가 다른 일시적인 작업에 사용할 수없고 스레드 풀이 다른 스레드를 시작하는 동안 이러한 임시 작업 중 하나를 시작하도록 지연시킬 수 있음을 의미합니다 (적어도 둘째).

BGW (또는 EAP 또는 APM)가 더 이상 사용되지 않는다고 말하는 프레임 워크의 속성은 없습니다. 따라서 이러한 것들이 언제 어디에서 "쓸모없는"것인지 결정하는 것은 당신에게 달려 있습니다. BGW는 특히 항상 적용되는 매우 특정한 사용 시나리오를 가지고있었습니다. .NET 4.0 및 4.5에서 상당히 괜찮은 대안이 있습니다. 하지만 BGW가 "쓸모 없다"고 생각하지 않습니다.

BackgroundWorker 항상 사용하는 것은 아닙니다. BackgroundWorker 를 자동으로 사용 중지하기 전에 생각 하는 것이 좋을 수도 있습니다. 어떤 경우에는 더 나은 선택 일 수 있습니다.





async-await