await와 ContinueWith의 차이점


119

다음 예에서 누군가 awaitContinueWith동의어인지 아닌지 설명 할 수 있습니까 ? 나는 처음으로 TPL을 사용하려고 노력하고 있으며 모든 문서를 읽었지만 차이점을 이해하지 못합니다.

기다립니다 :

String webText = await getWebPage(uri);
await parseData(webText);

ContinueWith :

Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) =>  parseData(task.Result));
webText.Start();
continue.Wait();

특정 상황에서 하나가 다른 것보다 선호됩니까?


3
당신이 제거 된 경우 Wait두 번째 예제에서 전화를 한 후 두 조각은 (대부분) 해당 될 것이다.
Servy


참고 : 귀하의 getWebPage방법은 두 코드 모두에서 사용할 수 없습니다. 첫 번째 코드에는 Task<string>반환 유형이 있고 두 번째 코드 에는 string반환 유형이 있습니다. 따라서 기본적으로 코드가 컴파일되지 않습니다. -정확하다면.
Royi Namir

답변:


101

두 번째 코드에서는 연속 작업이 완료 될 때까지 동 기적으로 대기합니다. 첫 번째 버전에서 메서드는 await아직 완료되지 않은 첫 번째 표현식에 도달 하자마자 호출자에게 반환됩니다 .

둘 다 연속을 예약한다는 점에서 매우 유사하지만 제어 흐름이 약간 복잡해지면 코드 await훨씬 더 간단 해 집니다 . 또한 Servy가 주석에서 언급했듯이 작업을 기다리는 것은 일반적으로 더 간단한 오류 처리로 이어지는 집계 예외를 "언 래핑"합니다. 또한를 사용 await하면 호출 컨텍스트에서 연속을 암시 적으로 예약합니다 (를 사용하지 않는 경우 ConfigureAwait). "수동으로"수행 할 수없는 것은 아니지만 .NET으로 수행하는 것이 훨씬 쉽습니다 await.

난 당신이 모두 작업의 약간 큰 순서를 구현하려고 제안 await하고 Task.ContinueWith- 그것은 진짜 눈을 뜨게 할 수있다.


2
두 조각 사이의 오류 처리도 다릅니다. 일반적으로 그 점에서 awaitover 와 함께 작업하는 것이 더 쉽습니다 ContinueWith.
Servy

@Servy : 사실, 그 주위에 뭔가를 추가합니다.
Jon Skeet 2013 년

1
스케줄링은 또한 매우 다르다 즉, 어떤 상황 parseData에서 실행합니다.
스티븐 클리어 리

await를 사용하면 호출 컨텍스트에서 연속을 암시 적으로 예약 한다고 말할 때 그 이점과 다른 상황에서 어떤 일이 발생하는지 설명 할 수 있습니까?
Harrison

4
@Harrison : WinForms 앱을 작성한다고 가정 해 봅시다. 비동기 메서드를 작성하면 기본적으로 메서드 내의 모든 코드가 UI 스레드에서 실행됩니다. 그 이유는 계속이 거기에서 예약되기 때문입니다. 연속 실행을 원하는 위치를 지정하지 않으면 기본값이 무엇인지 모르지만 스레드 풀 스레드에서 쉽게 실행될 수 있습니다 ... UI에 액세스 할 수없는 지점 등 .
Jon Skeet

100

다음은 비동기 해결을 사용하여 차이점과 다양한 문제를 설명하기 위해 최근에 사용한 코드 조각의 시퀀스입니다.

GUI 기반 애플리케이션에 많은 시간이 걸리는 이벤트 핸들러가있어서이를 비동기로 만들고 싶다고 가정 해 보겠습니다. 시작하는 동기 논리는 다음과 같습니다.

while (true) {
    string result = LoadNextItem().Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
        break;
    }
}

LoadNextItem은 작업을 반환하며 결국 검사하려는 결과를 생성합니다. 현재 결과가 찾고있는 결과이면 UI에서 일부 카운터 값을 업데이트하고 메서드에서 반환합니다. 그렇지 않으면 LoadNextItem에서 더 많은 항목을 계속 처리합니다.

비동기 버전에 대한 첫 번째 아이디어 : 계속 사용하십시오! 그리고 당분간 루핑 부분을 무시합시다. 내 말은, 무엇이 잘못 될 수 있습니까?

return LoadNextItem().ContinueWith(t => {
    string result = t.Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
    }
});

좋습니다. 이제 차단하지 않는 메서드가 있습니다! 대신 충돌합니다. UI 컨트롤에 대한 모든 업데이트는 UI 스레드에서 발생해야하므로이를 고려해야합니다. 고맙게도 연속 작업을 예약하는 방법을 지정하는 옵션이 있으며 이에 대한 기본 옵션이 있습니다.

return LoadNextItem().ContinueWith(t => {
    string result = t.Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
    }
},
TaskScheduler.FromCurrentSynchronizationContext());

좋습니다. 이제 충돌하지 않는 방법이 있습니다! 대신 조용히 실패합니다. 연속 작업은 이전 작업의 상태와 관련이없는 별도의 작업 자체입니다. 따라서 LoadNextItem에 오류가 있더라도 호출자는 성공적으로 완료된 작업 만 볼 수 있습니다. 좋아요, 예외가 있다면 그냥 넘겨주세요 :

return LoadNextItem().ContinueWith(t => {
    if (t.Exception != null) {
        throw t.Exception.InnerException;
    }
    string result = t.Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
    }
},
TaskScheduler.FromCurrentSynchronizationContext());

이제 실제로 작동합니다. 단일 항목의 경우. 이제 루핑은 어떻습니까? 결과적으로 원래 동기 버전의 논리와 동일한 솔루션은 다음과 같습니다.

Task AsyncLoop() {
    return AsyncLoopTask().ContinueWith(t =>
        Counter.Value = t.Result,
        TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
    var tcs = new TaskCompletionSource<int>();
    DoIteration(tcs);
    return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
    LoadNextItem().ContinueWith(t => {
        if (t.Exception != null) {
            tcs.TrySetException(t.Exception.InnerException);
        } else if (t.Result.Contains("target")) {
            tcs.TrySetResult(t.Result.Length);
        } else {
            DoIteration(tcs);
        }});
}

또는 위의 모든 것 대신 async를 사용하여 동일한 작업을 수행 할 수 있습니다.

async Task AsyncLoop() {
    while (true) {
        string result = await LoadNextItem();
        if (result.Contains("target")) {
            Counter.Value = result.Length;
            break;
        }
    }
}

이제 훨씬 더 멋지지 않습니까?


감사합니다, 정말 좋은 설명
Elger Mensonides 2014 년

이 좋은 예입니다
Royi Namir
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.