[C#] C #에서 “return await”의 목적은 무엇입니까?

거기에 어떤 과 같은 방법을 쓰는 시나리오 :

public async Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return await DoAnotherThingAsync();
}

이 대신에 :

public Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return DoAnotherThingAsync();
}

말이 될까요?

내부 호출 에서 return await직접 리턴 할 때 구문을 사용하는 이유는 무엇 입니까?Task<T>DoAnotherThingAsync()

return await많은 곳에서 코드가 표시 됩니다. 무언가를 놓친 것 같습니다. 그러나 내가 이해하는 한,이 경우 async / await 키워드를 사용하지 않고 직접 작업을 반환하는 것은 기능적으로 동일합니다. 왜 추가 await레이어의 오버 헤드를 추가 해야합니까?



답변

하나 부적절한 케이스가 return정상적인 방법과 return await에서 async다르게 동작합니다 방법은 :와 결합 될 때 using(또는보다 일반적으로, 임의의 return awaitA의 try블록).

다음 두 가지 버전의 메소드를 고려하십시오.

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

첫 번째 방법 것이다 객체 즉시로 실제로 완료되기 전에 긴 가능성이 메소드가 복귀. 즉, 첫 번째 버전은 버그 가 많을 수 있으며 ( 너무 빨리 폐기 되므로 ) 두 번째 버전은 제대로 작동합니다.Dispose()FooDoAnotherThingAsync()Foo


답변

필요하지 않은 경우 async(즉, Task직접 반품 할 수 있는 경우)를 사용하지 마십시오 async.

두 가지 비동기 작업을 수행하는 return await경우와 같이 유용한 상황 이 있습니다.

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

async성능 에 대한 자세한 내용 은 Stephen Toub의 MSDN 기사 및 주제에 대한 비디오참조하십시오 .

업데이트 : 나는 훨씬 더 자세히 블로그 게시물 을 작성했습니다 .


답변

await이전 코드에 다른 것이 있거나 결과를 반환하기 전에 어떤 식 으로든 조작하려는 경우에만 그렇게해야합니다. 발생할 수있는 또 다른 방법은 try/catch예외 처리 방식을 변경하는 것입니다. 그중 아무것도하지 않으면 옳습니다. 메소드를 만드는 오버 헤드를 추가 할 이유가 없습니다 async.


답변

결과를 기다리는 또 다른 경우는 다음과 같습니다.

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

이 경우 두 방법간에 유형 이 다르고에 직접 할당 할 수 없기 때문에 GetIFooAsync()결과를 기다려야합니다 . 당신이 결과를 기다리고한다면, 그것은 단지하게 되는 것입니다 직접 할당 . 그런 다음 비동기 메소드는 결과를 내부 와 외부로 다시 패키지합니다 .GetFooAsyncTTask<Foo>Task<IFoo>FooIFooTask<IFoo>


답변

그렇지 않으면 간단한 “썽크”메소드를 비동기로 만들면 비동기 상태 머신은 메모리에 생성되지만 비동기 상태 머신은 그렇지 않습니다. 비효율적 인 버전을 사용하는 사람들이 종종 비효율적 인 버전을 사용하도록 지시 할 수 있지만, 더 효율적이기 때문에 중단되는 경우 해당 방법이 “반환 / 연속 스택”에 관련되어 있다는 증거가 없습니다. 때로는 중단을 이해하기가 더 어려워집니다.

따라서 perf가 중요하지 않은 경우 (그리고 일반적으로 그렇지 않은 경우) 나중에이 중단을 진단하는 데 도움이되는 비동기 상태 시스템을 사용하여 나중에 중단을 진단하고, 썽크 방식은 시간이 지남에 따라 발전해 왔기 때문에 던져지는 대신 오류가 발생한 작업을 반환해야합니다.


답변

return await를 사용하지 않으면 디버깅 중 또는 예외 로그에 인쇄 될 때 스택 추적을 망칠 수 있습니다.

작업을 반환하면 메서드가 목적을 달성했으며 호출 스택에서 벗어났습니다. 사용 return await하면 통화 스택에 그대로 둡니다.

예를 들면 다음과 같습니다.

Await 사용시 호출 스택 : A가 B에서 작업을 기다리는 중 => B가 C에서 작업을 기다리는 중

await를 사용 하지 않을 때의 호출 스택 : A가 B가 리턴 한 C의 태스크를 기다리고 있습니다.


답변

이것은 또한 나를 혼란스럽게하며 이전 답변이 실제 질문을 간과했다고 생각합니다.

내부 DoAnotherThingAsync () 호출에서 Task를 직접 반환 할 수있는 경우 return await 구문을 사용하는 이유는 무엇입니까?

때로는 실제로를Task<SomeType>하지만 대부분의 경우 실제로 인스턴스 SomeType, 즉 작업의 결과를 원합니다 .

코드에서 :

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

구문에 익숙하지 않은 사람 (예를 들어, 나)은이 메소드가을 리턴해야한다고 생각할 수도 Task<SomeResult>있지만,로 표시되어 있기 때문에 async실제 리턴 유형은 SomeResult입니다. 을 사용 return foo.DoAnotherThingAsync()하면 컴파일되지 않은 작업이 반환됩니다. 올바른 방법은 작업 결과를 반환하는 것입니다 return await.