기다리다 Task.Wait-교착 상태?


197

꽤 차이 이해가 안 Task.Wait와 await.

ASP.NET WebAPI 서비스에 다음 기능과 비슷한 것이 있습니다.

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}

Get교착 상태는 어디 입니까?

무엇이 이것을 일으킬 수 있습니까? 차단 대기 시간을 사용할 때 왜 이것이 문제가되지 await Task.Delay않습니까?


@ Servy : 시간이 되 자마자 repo와 함께 돌아올 것입니다. 지금 Task.Delay(1).Wait()은 충분히 작동합니다 .
— ronag

2
Task.Delay(1).Wait()기본적으로와 정확히 같은 것 Thread.Sleep(1000)입니다. 실제 생산 코드에서는 거의 적합하지 않습니다.
— Servy

@ronag : WaitAll교착 상태가 발생했습니다. 자세한 내용은 답변에서 내 블로그 링크를 참조하십시오. await Task.WhenAll대신 사용해야 합니다.
— Stephen Cleary

6
@ronag 교착 상태에 ConfigureAwait(false)대한 단일 호출이 Bar있거나 Ros교착 상태가 발생하지 않지만 열거 형이 하나 이상 생성 된 다음 모든 항목을 기다리고 있기 때문에 첫 번째 막대는 두 번째 교착 상태를 교착 상태로 만듭니다. 당신이 경우 await Task.WhenAll대신 당신이 ASP 컨텍스트를 차단하지 그래서, 모든 작업을 기다리고, 당신은 방법의 반환을 정상적으로 볼 수 있습니다.
— Servy

2
@ronag 귀하의 다른 옵션을 추가하는 것입니다 .ConfigureAwait(false) 트리까지 모든 방법을 당신이 그런 식으로 아무것도입니다 차단 될 때까지 지금 다시 주요 문맥을 얻으려고 노력하지; 작동합니다. 또 다른 옵션은 내부 동기화 컨텍스트를 스핀 업하는 것입니다. 링크 . 당신이를 넣으면 Task.WhenAll에 AsyncPump.Run효과적으로 당신이 필요없이 모든 일에 차단 ConfigureAwait어디서나, 그러나 아마 지나치게 복잡한 해결책이.
— Servy

답변:


270

Wait와 await- 개념적으로 유사한 동안 - 사실은 완전히 다릅니다.

Wait작업이 완료 될 때까지 동 기적으로 차단합니다. 따라서 현재 스레드는 작업이 완료되기를 기다리는 그대로 문자 그대로 차단됩니다. 일반적으로 "완료"를 사용해야합니다 async. 즉, async코드를 차단하지 마십시오 . 내 블로그 에서 비동기 코드 차단이 교착 상태를 일으키는 방법 에 대한 세부 정보를 살펴 봅니다 .

await작업이 완료 될 때까지 비동기 적으로 기다립니다. 이는 현재 메소드 가 "일시 중지됨"(상태가 캡처 됨)이며 메소드가 불완전한 태스크를 호출자에게 리턴 함을 의미합니다. 나중에 await식이 완료되면 나머지 메서드는 연속으로 예약됩니다.

또한 "협업 블록"을 언급했는데, 여기서는 현재 작업중인 작업이 Wait대기 스레드에서 실행될 수 있다고 가정합니다 . 이런 상황이 발생할 수 있지만 최적화입니다. 작업이 다른 스케줄러 용이거나 이미 시작되었거나 코드가 아닌 작업 인 경우와 같이 (예 : 코드 가 없으므로 인라인으로 작업을 실행할 수없는 경우) 발생할 수없는 상황이 많이 있습니다 . 그것을 위해).WaitDelay

내 async/ await소개가 도움 이 될 수 있습니다.


5
나는 오해가 있다고 생각하고 Wait훌륭한 await교착 상태를 작동 시킵니다 .
— ronag

1
분명히 : 내 대체하는 경우 예 await Task.Delay(1)와 Task.Delay(1).Wait()서비스 그렇지 않으면 교착, 잘 작동합니다.
— ronag

5
아니오, 작업 스케줄러는 그렇게하지 않습니다. Wait스레드를 차단하고 다른 용도로는 사용할 수 없습니다.
— Stephen Cleary

8
@ronag 내 생각에 방금 메서드 이름이 섞여 있고 교착 상태가 실제로 차단 코드로 인해 코드로 작동했습니다 await. 또는 교착 상태가 둘 중 하나와 관련이 없으며 문제를 잘못 진단했습니다.
— Servy

3
@ hexterminator : 이것은 의도적으로 설계된 것입니다 .UI 앱에는 효과적이지만 ASP.NET 앱에는 방해가되는 경향이 있습니다. ASP.NET Core는을 제거하여이 문제를 해결 SynchronizationContext했으므로 ASP.NET Core 요청 내에서 차단하면 더 이상 교착 상태가 발생하지 않습니다.
— Stephen Cleary

5

다른 출처에서 읽은 내용을 기반으로합니다.

await표현은 실행중인 스레드를 차단하지 않는다. 대신, 컴파일러가 나머지 async메소드를 대기중인 태스크의 연속으로 등록하게합니다. 그런 다음 제어는 async메소드 의 호출자에게 리턴합니다 . 작업이 완료되면 작업이 계속 진행되고 async중단 된 지점 에서 메소드 실행이 재개됩니다.

싱글 task이 완료 되기를 기다리려면 Task.Wait메소드를 호출하면 됩니다. 받는 호출 Wait단일 클래스 인스턴스까지 메소드는 블록 호출 스레드가 실행을 완료했습니다. 매개 변수가없는 Wait()방법은 작업이 완료 될 때까지 무조건 대기하는 데 사용됩니다. 이 작업 Thread.Sleep은 2 초 동안 휴면 모드를 호출하여 작업을 시뮬레이션합니다 .

이 기사 도 잘 읽었습니다.


3
"그런게 기술적으로 틀리지 않습니까? 다른 사람이 명확하게 설명해 주시겠습니까?" -명확히 할 수 있습니까? 질문으로 그 질문을합니까? (단지 당신이 묻는 지 대답하고 있는지 명확하게하고 싶습니다). 당신이 묻는다면 : 그것은 별도의 질문으로 더 잘 작동 할 수 있습니다. 답변으로 여기에 새로운 답변을 수집 할 것 같지 않습니다
— Marc Gravell

1
나는 질문에 대답하고 여기에있는 의심에 대해 별도의 질문을했다 stackoverflow.com/questions/53654006/… 감사합니다 @MarcGravell. 답변에 대한 삭제 투표를 제거 할 수 있습니까?
— Ayushmati

"지금 답변에 대한 삭제 투표를 제거 할 수 있습니까?" -그건 내 것이 아니다. ♦ 덕분에 본인의 투표는 즉시 효력을 발휘했을 것입니다. 그러나 이것이 교착 상태 행동에 관한 질문의 핵심 포인트에 대한 답변이라고 생각하지 않습니다.
— Marc Gravell

사실이 아닙니다. 처음 기다릴 때까지 모든 것이 차단됩니다
— user1785960

-2

다른 답변에는 몇 가지 중요한 사실이 제시되지 않았습니다.

"비동기 대기"는 CIL 수준에서 더 복잡하므로 메모리 및 CPU 시간이 걸립니다.

대기 시간이 허용되지 않으면 모든 작업을 취소 할 수 있습니다.

"async await"의 경우이를 취소하거나 모니터링 할 수있는 작업이 없습니다.

"비동기 대기"보다 작업을 사용하는 것이 더 유연합니다.

모든 동기화 기능은 비동기로 래핑 될 수 있습니다.

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 

"비동기 대기"는 많은 문제를 발생시킵니다. 우리는 이제 런타임 및 컨텍스트 디버깅없이 await 문에 도달하지 않습니다. 처음에 도달하지 않으면 모든 것이 차단 됩니다. 때로는 기다릴지라도 여전히 모든 것이 차단됩니다.

https://github.com/dotnet/runtime/issues/36063

동기화 및 비동기 방법이나 해킹을 사용하기 위해 코드 복제로 살아야하는 이유를 모르겠습니다.

결론 : 수동으로 작업을 생성하고 제어하는 ​​것이 훨씬 좋습니다. 처리기 대 작업은 더 많은 제어를 제공합니다. 작업을 모니터링하고 관리 할 수 ​​있습니다.

https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem

내 영어 죄송합니다.

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