programing

작업을 직접 반환할 수 있는데 비동기를 사용하고 반환을 기다리는 이유는 무엇입니까?

sourcejob 2023. 5. 29. 10:37
반응형

작업을 직접 반환할 수 있는데 비동기를 사용하고 반환을 기다리는 이유는 무엇입니까?

다음과 같은 작성 방법이 있는 시나리오가 있습니까?

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너무 많은 곳에서 뭔가 놓친 것 같아요를 놓친 것 같아요.하지만 이 경우 비동기/대기 키워드를 사용하지 않고 작업을 직접 반환하는 것은 기능적으로 동등한 것으로 알고 있습니다.입니까?await 레이어? 레이어?

한 가지 교활한 경우가 있습니다.return 와 정적인방그리고로 ▁in▁normal.return awaitasync방법이 다르게 동작함: 결합된 경우using더 임의의 (또는, 더일으로적, 임의반)return await순식간에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()그자리의 Foo즉시 반대합니다.DoAnotherThingAsync()메서드가 반환됩니다. 이는 실제로 완료되기까지 오래 걸릴 수 있습니다.이것은 첫 번째 버전이 아마도 버그가 있다는 것을 의미합니다(왜냐하면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().GetFooAsync 의종 유형이기 입니다.T두 가지 방법이 서로 다릅니다.Task<Foo>는 에 직접 할당할 수 .Task<IFoo>하지만 당신이 결과를 기다리면, 그것은 그냥.Foo 직접 할당할 수 있는.IFoo그러면 비동기 방식은 결과를 내부에 재포장합니다.Task<IFoo>그리고 당신은 떠나가세요.

return wait를 사용하지 않을 경우 디버깅 중이나 예외 시 로그에 출력되는 스택 추적이 손상될 수 있습니다.

작업을 반환할 때 메소드는 목적을 달성하고 콜 스택에서 제외됩니다.사용할 때return await당신은 그것을 콜 스택에 두고 가는 것입니다.

예:

사용 시 호출 스택 대기:A가 B로부터 작업 대기 중 => B가 C로부터 작업 대기 중

사용하지 않을 때 호출 스택 대기:A가 C로부터 태스크를 대기 중이며, B가 반환했습니다.

단순한 "thunk" 메서드를 비동기로 만들면 메모리에 비동기 상태 시스템이 생성되는 반면 비비동기는 생성되지 않습니다.이는 비 비동기 버전이 더 효율적이기 때문에 사람들이 비 비동기 버전을 사용하는 것을 가리킬 수 있지만(사실), 중단이 발생할 경우 해당 방법이 "반환/계속 스택"과 관련되어 있다는 증거가 없다는 것을 의미하기도 합니다. 이는 때때로 중단을 이해하는 것을 더 어렵게 만듭니다.

성능이 중요하지 않을 때(일반적으로 그렇지 않을 때) 이러한 모든 thunk 메서드에 비동기식을 적용하여 나중에 행을 진단하고 시간이 지남에 따라 이러한 thunk 메서드가 발전할 경우 장애가 발생한 작업을 폐기하는 대신 반환하도록 지원합니다.

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

내부 DoAnother에서 작업을 직접 반환할 수 있는데 사용 반환을 기다리는 이유ThingAsync() 호출?

음, 가끔 당신은 정말로 원하는 것은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.

당신이 원하는 또 다른 이유는return awaitawait구문을 사용하면 일치하지 않을 수 있습니다.Task<T>그리고.ValueTask<T>가 유형을 반환하더라도 합니다. 예를 들어, SubTask 메서드가 다음을 반환하더라도 아래 코드는 작동합니다.Task<T>하지만 그것의 발신자는 돌아옵니다.ValueTask<T>.

async Task<T> SubTask()
{
...
}

async ValueTask<T> DoSomething()
{
  await UnimportantTask();
  return await SubTask();
}

대기 시간을 건너뛰면,DoSomething() "", "" CS0029:

'System' 유형을 암시적으로 변환할 수 없습니다.스레드화.Tasks.Task <BlaBla>에서 'System'으로 이동합니다.스레드화.Tasks.ValueTask<BlaBla>.

명시적으로 타이프 캐스트를 하려고 하면 CS0030도 나옵니다.

참고로 이건 .NET Framework입니다."그것은 .NET 가상 버전에서 고정되었습니다."라는 댓글을 완전히 예상할 수 있습니다, 저는 그것을 테스트하지 않았습니다. :)

비대기 방법의 또 다른 문제는 특히 다음과 같은 경우에 반환 유형을 암시적으로 캐스팅할 수 없다는 것입니다.Task<IEnumerable<T>>:

async Task<List<string>> GetListAsync(string foo) => new();

// This method works
async Task<IEnumerable<string>> GetMyList() => await GetListAsync("myFoo");

// This won't work
Task<IEnumerable<string>> GetMyListNoAsync() => GetListAsync("myFoo");

오류:

'System' 유형을 암시적으로 변환할 수 없습니다.스레드화.Tasks.Task<시스템.컬렉션.포괄적인.List >'에서 'System'으로 이동합니다.스레드화.Tasks.Task<시스템.컬렉션.포괄적인.'IE numberable >'

언급URL : https://stackoverflow.com/questions/19098143/why-use-async-and-return-await-when-you-can-return-taskt-directly

반응형