23

나는 새로운 비동기에 대해 읽었습니다.await키워드를 사용하고 멋지게 들리지만, 지금까지 본 인트로 비디오 중 하나에 대한 답을 찾을 수 없었던 핵심 질문이 하나 있습니다 (필자도 백서를 읽어보십시오).

전화가 있다고 가정 해 봅시다.await기본 UI 스레드에서 중첩 된 함수에서. 이 시점에서 스레드는 어떻게됩니까? 제어가 메시지 루프로 돌아가고 UI 스레드가 다른 입력을 처리 할 수 있습니까?

대기중인 작업이 완료되면 전체 스택이 메시지 대기열로 밀어 넣어 져서 해당 중첩 함수를 통해 제어가 반환되거나 여기서 완전히 발생하는 것입니까?

그리고 두 번째로 (내가주의를 기울이는 동안) 비동기 메소드를 왜 라벨링해야하는지 이해하지 못한다.async. 어떤 메서드를 비동기 적으로 실행할 수 있습니까? 메서드를 비동기 적으로 실행하려고하지만 비동기 키워드가없는 경우에는 어떻게해야합니까? 단순히이를 수행 할 수있는 방법이 있습니까?

건배. :)

편집하다:필자가 샘플 코드를 컴파일 할 수 있다면, 아마 나 자신을 알아낼 수있을 것이다. 그러나 한 가지 이유 또는 다른 이유로 나는 블록을 실행 중이다. 내가 정말로 알고 싶은 것은 지속이 어느 정도 지속 되는가하는 것입니다 ... 전체 호출 스택을 고정 시키거나, 작업이 완료 될 때 다시 시작하거나, 아니면 지금까지만 돌아갈 수 있습니까? 연속을 지원하기 위해 함수 자체를 비동기로 표시해야합니까, 아니면 원래 요청했듯이 전체 호출 스택을 계속 수행합니까?

그 경우하지 않는다비동기 호출 함수가 비동기 호출을 기다리는 경우 어떻게됩니까? 거기 블록합니까? 기다리고있는 시점을 패배시키지 않겠습니까? 나는 당신이 내가 누군가를 채울 수 있기를 바란다는 것을 여기서 이해하지 못한다는 것을 알기를 바랍니다. 그래서 나는 이것을 계속 배울 수 있습니다.


  • 에릭 리 퍼트 (Eric Lippert)블로그 게시물대기 / 비동기 작동 방식 이 질문은 귀하의 모든 질문에 대답해야합니다. - dtb
  • Jon Skeet은 또한 일련의게시물그는 비동기식 CTP를 거쳐 기본적으로 다시 구현됩니다. 그는 또한 몇 가지 시나리오에 대한 컴파일러 생성 코드를 보여줍니다. - bbogovich
  • 두 링크는 더 이상 유효하지 않으며 Eric Lippert의 블로그 게시물은 현재이리그리고 Eduasync는 지금이다.이리 - dlatikay

2 답변


26

기본 UI 스레드에서 중첩 된 함수를 기다리는 호출이 있다고 가정합니다. 이 시점에서 스레드는 어떻게됩니까? 제어가 메시지 루프로 돌아가고 UI 스레드가 다른 입력을 처리 할 수 있습니까?

예. 때를await기다릴만한 (예 :Task<TResult>), 스레드 내의 현재 스레드 위치async메소드가 캡처됩니다. 그런 다음 대기 상태가 완료 될 때 실행될 메소드의 나머지 ( "계속")를 대기열에 넣습니다 (예 :Task<TResult>완료).

그러나, 수행 할 수있는 최적화가 있습니다 : 대기가 이미 완료된 경우await기다릴 필요가 없으며 메서드를 즉시 계속 실행합니다. 이를 "빠른 경로"라고합니다.여기에 설명되어있다..

대기중인 작업이 완료되면 전체 스택이 메시지 대기열로 밀어 넣어 져서 해당 중첩 함수를 통해 제어가 반환되거나 여기서 완전히 발생하는 것입니까?

스레드의 현재 위치가 UI 메시지 큐에 푸시됩니다. 세부 사항은 조금 더 복잡합니다.TaskScheduler.FromCurrentSynchronizationContext~ 않는 한SynchronizationContext.Current~이다.null, 그 경우에 그들은 예정되어있다.TaskScheduler.Current. 또한이 동작을 호출하여 재정의 할 수 있습니다.ConfigureAwait(false)이는 항상 스레드 풀에서 연속을 예약합니다. 이후SynchronizationContext.CurrentUISynchronizationContextWPF / WinForms / Silverlight의 경우이 연속체가 UI 메시지 큐에 푸시됩니다.

그리고 두 번째로 (내가주의를 끄는 동안) 비동기 메서드를 비동기로 레이블링해야하는 이유를 실제로 이해하지 못합니다. 어떤 메서드를 비동기 적으로 실행할 수 있습니까? 메서드를 비동기 적으로 실행하려고하지만 비동기 키워드가없는 경우에는 어떻게해야합니까? 단순히이를 수행 할 수있는 방법이 있습니까?

이들은 "비동기"라는 단어와 약간 다른 의미입니다. 그만큼async키워드를 사용하면await예어. 다른 말로,async메소드는await. 구식 비동기 대리자 (예 :BeginInvoke/EndInvoke)는async. 비동기 대리자는ThreadPool실,하지만async메서드는 UI 스레드에서 실행됩니다 (UI 컨텍스트에서 호출되고 호출하지 않는다고 가정).ConfigureAwait(false)).

당신이 (비 -async) 메소드는ThreadPool스레드, 당신은 이렇게 할 수 있습니다 :

await Task.Run(() => MyMethod(..));

내가 정말로 알고 싶은 것은 지속이 어느 정도 지속 되는가하는 것입니다 ... 전체 호출 스택을 고정 시키거나, 작업이 완료 될 때 다시 시작하거나, 아니면 지금까지만 돌아갈 수 있습니까? 연속을 지원하기 위해 함수 자체를 비동기로 표시해야합니까, 아니면 원래 요청했듯이 전체 호출 스택을 계속 수행합니까?

현재 위치가 캡처되고 연속 실행시 "다시 시작"됩니다. 사용하는 모든 함수await계속을 표시하려면 반드시 표시해야합니다.async.

전화하면async방법은 비 -async방법을 사용하면Task개체를 직접. 이것은 정상적으로 수행되지 않습니다. 최상위async메서드가 반환 할 수 있습니다.void, 그렇게하지 않을 이유가 없습니다.async이벤트 핸들러.

유의 사항async순전히 컴파일러 변환입니다. 즉,async메서드는 컴파일 된 후 일반 메서드와 정확하게 같습니다. .NET 런타임은 특별한 방법으로 그들을 처리하지 않습니다.


  • 호출 스택에 대해서는 사실이 아니라고 생각합니다. 표현식이 분명히 발생하여 yield가 발생 된 후에 예외가 발생하면 원래 호출 스택에 있던 catch 블록에 의해 예외가 캡처되지 않습니다. - Damien_The_Unbeliever
  • 아니; 방금 100 % 확신을 얻으려고 시험을 보았습니다. 예외는 잘 잡혔다. 예외는 다음에 래핑됩니다.Task, 그래서 당신은 할 필요가있다.await그것은 예외를 볼 수 있습니다. 그런 다음 호출 스택 위로 이동합니다. - Stephen Cleary
  • 호출 스택의 호출자 중 전체 (또는 대부분)가 모두 비동기로 변환 된 상황에 대해 이야기하고 있다고 생각합니다. OP의 헤드에 이미지가 있는지 여부는 확실하지 않습니다. async 메서드를 호출하고 작업을 반환하는 비 비동기 코드 스택을 보여주고있었습니다. 그 태스크가 리턴되면 (그리고 비동기 메소드가 완료되기 전에 " 전에 "), 스택의 비동기 메소드가 모두 완료됩니다. - Damien_The_Unbeliever
  • 네가하는 말을 안다. 이 경우 예외는Task개체, 나중에 관찰 할 수 있습니다. 전체 스택이 캡처 된 경우에도 (이 경우에는 그렇지 않습니다.)catch예외가 실행되기 때문에 블록을 실행할 수 없습니다.Task. - Stephen Cleary

4

그것은 Awaitable의 행동에 달려 있습니다.

동기 실행 옵션이 있습니다. 즉, 스레드에서 실행되고 동일한 스레드에서 대기 상태로 제어를 되돌립니다.

비동기 적으로 실행되도록 선택하면 기다리는 시간이 콜백을 예약하는 스레드에서 대기자가 다시 호출됩니다. 그 동안 호출 스레드는 비동기 작업을 시작하고 대기 상태에 빠져 있기 때문에 대기 상태가 해제되고 대기 상태는 계속 대기 상태의 콜백에 연결되어 있기 때문에 해제됩니다.

두 번째 질문에서 async 키워드는 메서드가 비동기 적으로 호출되는지 아닌지에 대한 것이 아니라 해당 메서드의 본문이 비동기 코드 인라인 자체를 호출하려고하는지 여부와 관련이 없습니다.

나는. Task 또는 Task를 반환하는 모든 메서드는 비동기 적으로 (기다리거나 계속 계속됨) 호출 할 수 있지만 async로 표시하면 해당 메서드는 본문에서 await 키워드를 사용할 수 있으며 반환 할 때 Task를 반환하지는 않지만 단순히 반환합니다. T는 전체 본문이 Task가 실행하는 상태 시스템으로 다시 작성되기 때문에.

방법이 있다고 가정 해 봅시다.

public async Task DoSomething() {
}

UI 스레드에서 호출하면 작업이 반환됩니다. 이 시점에서 다음을 사용하여 작업을 차단할 수 있습니다..Wait()또는.ResultTaskScheduler에서 async 메서드를 실행하거나.RunSynchronously()UI 스레드에서 실행됩니다. 물론 내부에서 일어나는 모든 일이 기다리고 있습니다.DoSomething기본적으로 또 다른 연속체이므로 TaskScheduler 스레드에서 코드의 일부를 실행하게 될 수도 있습니다. 그러나 결국 UI 스레드는 정상적인 동기 메서드와 마찬가지로 완료 될 때까지 차단됩니다.

또는 다음을 사용하여 일정을 예약 할 수 있습니다..ContinueWith()작업이 완료되면 TaskScheduler에 의해 호출되는 작업을 만듭니다. 이렇게하면 UI 스레드에서 수행중인 작업을 계속 수행하는 현재 코드로 컨트롤이 즉시 반환됩니다. 연속은 콜 스택을 캡처하지 않으며 단순히 액션 일 뿐이므로 외부 범위에서 액세스하는 모든 변수를 캡처합니다.


  • 제가 특별히 알고 싶은 것은 만약완전한callstack은 UI 스레드가 실제로 종료 할 수 없으므로 UI 스레드에서 어떻게 작동하는지 그리고 연속으로 설정됩니다. - devios1
  • 비동기 메서드는 컴파일러에서 상태 시스템으로 다시 씁니다. "정지"하는 방법이 " 기본적으로 상태 시스템은 범위에있는 모든 변수의 현재 상태를 캡처했습니다. 따라서 콜 스택 (callstack)에 아무 것도하지 않습니다. - Arne Claassen
  • UI 스레드 관련 : 비동기 메서드 (즉, 기다릴 수있는 유일한 장소)를 호출하면 작업이 반환됩니다. 따라서 UI 스레드에서 작업을 수행하는 경우 (UI 스레드 차단) 작업을 차단하거나 UI 스레드에서 계속할 수있는 ContinueWith를 추가하십시오 - Arne Claassen

연결된 질문


관련된 질문

최근 질문