2014-02-25 3 views
2

C#에서 비동기 메서드를 래핑하는 것과 관련하여 이상한 동작이 있습니다.비동기 메서드 랩

이 이상한 동작을하는 코드 부분을 잘라내는 것이 어렵 기 때문에 대신 테스트 프로젝트를 작성하여 문제를 조사했습니다. 이것이 내가 알아 낸 것입니다.

은 단순히 다른 비동기 방식의 래퍼입니다 비동기 방식을 가지고 테스트 클래스가 :

public class Test 
{ 
    public async Task Delay() 
    { 
     await Task.Delay(1000); 
    } 
} 
(내 원래 코드에서 그것은에 대한 래퍼 메소드가이 객체를 포함하는 래퍼 클래스입니다)

var test = new Test(); 
await Task.Delay(1000); 
await test.Delay(); 
Task.Delay(1000).Wait(); 
test.Delay().Wait(); 

모두 마지막 줄 때까지 잘 (나는 WPF 창을 사용하고 있기 때문에, 내 경우에는로드 된 경우) : 내 테스트 프로그램에서

나는 비동기 이벤트 처리기에서 다음 코드를 실행하고 결코 돌아 오지 않는다. 첫 번째 시나리오가 작동하지 않는 이유

public class Test 
{ 
    public Task Delay() 
    { 
     return Task.Delay(1000); 
    } 
} 

내 질문은 :

은 그 때 나는 작동 다음과 마지막 줄에 테스트 클래스를 변경하려고?

+3

이것은 "과부하"가 아닙니다 –

+0

당신이 옳을 수도 있습니다 (내가 생각할 수있는 가장 좋은 용어였습니다).하지만 더 나은 용어를 제공해 주시겠습니까? – reSPAWNed

+1

"wrapping"일까요? –

답변

4

기본함으로써 await 키워드는, 같은 원래의 스레드에서 실행 가능한 원본 코드의 대부분 (또는 유지하려고하는 내장되어 최소한의 await 전에 실행되는 코드와 같은 "상황"에 예를 들어 이전에 스레드 풀에서 실행 중이었던 경우 모든 스레드 풀 스레드가 수행합니다.

원래 코드가 실행될 수 있으면 UI 스레드에서 실행하려고합니다.

의 UI 스레드를 차단하므로 async 메서드 내에서 나머지 코드를 실행할 수 없으므로 바깥 쪽 Task을 완료하십시오. 나는.

continueOnCapturedContext을 :

public class Test 
{ 
    public async Task Delay() 
    { 
     await Task.Delay(1000); 
     //<-- This "Code" needs to run before my Task is completed 
    } 
} 

당신은 이런 일이하지 않는 것이 요청 ConfigureAwait(continueOnCapturedContext)를 사용할 수있는 다음 Task.Delay 작업이 완료된 후 코드를 그냥이 조금이 것은, 당신의 방법에서 실행 왼쪽

사실 마침내 마샬링을 시도해보십시오. 캡처 된 엄격한 상황; 그렇지 않으면 거짓입니다.

방법의 "비동기"수정이 방법은 자동으로 실행되도록 예약 "을 의미하지 않습니다


또한보십시오 에릭 Lippert의에 의해이 오래된 blog postasync 새로운에서 때 비동기 적으로 작업자 스레드 "라고합니다.그것은과 반대 인 을 의미합니다. 즉, "이 메서드는 비동기 작업을 기다리는 것을 포함하는 제어 흐름을 포함하므로 비동기 작업이 올바른 방식으로이 메서드를 다시 시작할 수 있도록 컴파일러에서 연속 통과 스타일로 다시 작성됩니다."전체적으로 비동기 메서드가 당신은 가능한 한 많이 현재 스레드에 머물러 있습니다.

+2

여기서 바람직한 해결책은'ConfigureAwait (false)'를 사용하는 것이 아니라 비동기 작업을 기다리지 않는 것입니다. – Servy

+0

나는 그 점을 볼 수 있습니다. 오히려 비동기로 표시하고 호출 된 비동기 메서드의 작업을 반환하지 않는 두 번째 시나리오를 사용 하시겠습니까? 하지만 여러 비동기 메서드를 호출하려면 어떻게해야합니까? – reSPAWNed

+1

@reSPAWNed 올바른 해결책은 Stephen의 대답이 아니고이 답변에 설명되어 있습니다. 전체 응용 프로그램을 비동기로 만들어야하며 비동기 및 동기 메서드를 함께 사용하지 않아야합니다. 당신이 그렇게한다면, 당신은이 문제를 전혀 가지지 않을 것입니다. – Servy

5

나는 세부 on my blog에와 MSDN article이 교착 상태 시나리오를 설명합니다. (이 경우에는 TaskScheduler.Current을 캡처 null하지 않는 한 SynchronizationContext.Current)를 await 작업, 그것은 현재의 "상황"을 캡처합니다 기본적으로

. async 메서드가 다시 시작되면 해당 컨텍스트에서 다시 시작됩니다.

경우에 따라 UI 컨텍스트가 캡처되고 지연 완료 후 async 메서드가 UI 스레드에서 다시 시작하려고합니다. 그러나 작업 완료를 기다리는 UI 스레드가 차단되어 실행을 다시 시작할 수 없습니다.

가장 좋은 해결책은 async "모든 방법"을 사용하는 것입니다. 즉, async 코드를 차단하지 마십시오.