누군가 설명 할 수있는 경우await
과ContinueWith
다음 예제에서는 동의어 또는 동일하지 않습니다. 나는 TPL을 처음 사용하려고 노력하고 있으며 모든 문서를 읽었지만 그 차이를 이해하지 못합니다.
기다리다:
String webText = await getWebPage(uri);
await parseData(webText);
계속하십시오:
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
특정 상황에서 다른 것보다 하나가 선호됩니까?
두 번째 코드에서는동 기적으로지속이 끝나기를 기다린다. 첫 번째 버전에서는 메서드가 첫 번째 버전과 충돌하는 즉시 호출자에게 반환됩니다.await
아직 완료되지 않은 표현.
그들은 둘 다 계속 일정을 잡는다는 점에서 매우 유사하지만, 제어 흐름이 약간 복잡해 지 자마자,await
~으로 이끌다많은더 간단한 코드. 또한 Servy가 주석에서 언급했듯이 작업을 기다리는 것은 일반적으로 간단한 오류 처리로 이어지는 집계 예외를 "해제"합니다. 또한 사용await
호출 컨텍스트에서 연속을 암시 적으로 예약합니다 (사용하지 않는 한).ConfigureAwait
). "수동으로"수행 할 수없는 것은 아니지만,await
.
둘 다 약간 더 큰 작업 순서를 구현하는 것이 좋습니다.await
과Task.ContinueWith
- 진짜 눈을 뜨게 될 수 있습니다.
await
위에ContinueWith
그 점에서. - ServyparseData
에서 실행됩니다. - Stephen Cleary
비동기 해결을 사용하여 차이점과 다양한 문제를 설명하기 위해 최근에 사용한 코드 스 니펫 시퀀스가 있습니다.
GUI 기반 애플리케이션에 시간이 많이 소요되는 이벤트 핸들러가 있고이를 비동기로 만들고 싶다고 가정 해 보겠습니다. 다음은 함께 시작하는 동기식 로직입니다.
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem은 검사 할 결과를 결국 생성하는 Task를 반환합니다. 현재 결과가 원하는 결과라면 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;
}
}
}
지금은 훨씬 좋네요, 그렇죠?
Wait
두 번째 예제에서 호출그때두 개의 미리보기는 (대부분) 동등한 것입니다. - ServygetWebPage
메소드를 두 코드 모두에서 사용할 수 없습니다. 첫 번째 코드에는Task<string>
반환 유형은 두 번째로string
리턴 유형. 기본적으로 코드는 컴파일되지 않습니다. - 정확하면. - Royi Namir