무효 반환과 작업 반환의 차이점은 무엇입니까?


128

다양한 C # 비동기 CTP 샘플을 보면 반환하는 일부 비동기 함수 void와 제네릭이 아닌 다른 함수를 반환 Task합니다. Task<MyType>비동기 작업이 완료되면 a를 반환하는 것이 호출자에게 데이터를 반환하는 데 유용한 이유를 알 수 있지만 반환 유형이있는 함수는 Task데이터를 반환하지 않습니다. 왜 돌아 오지 void않습니까?

답변:


214

SLaks와 Killercam의 답변은 좋습니다. 컨텍스트를 조금 더 추가하겠다고 생각했습니다.

첫 번째 질문은 본질적으로 어떤 방법을 표시 할 수 있는지에 관한 것 async입니다.

로 표시된 방법은 async반환 할 수 void, Task또는 Task<T>. 그들 사이의 차이점은 무엇입니까?

Task<T>반환 비동기 방법을 기다려온 할 수 있으며, 작업이 완료 될 때 그것은 T.을 신혼합니다

Task반환 비동기 방법을 기다려온 할 수 있으며, 작업이 완료는, 작업의 계속은 실행하도록 예약 할 때.

void반환 비동기 방법을 기다려온 할 수 없다; "화재와 잊어 버리기"방법입니다. 비동기식으로 작동하며 완료 시점을 알 수 없습니다. 이것은 조금 이상합니다. SLaks가 말했듯이 일반적으로 비동기 이벤트 핸들러를 만들 때만 수행합니다. 이벤트가 발생하면 핸들러가 실행됩니다. 이벤트 핸들러는 태스크를 리턴하지 않기 때문에 이벤트 핸들러가 리턴 한 태스크를 "기다릴 것"이 없으며, 수행 한 경우에도 어떤 코드가 태스크를 사용합니까? 일반적으로 컨트롤을 핸들러로 전송하는 것은 사용자 코드가 아닙니다.

의견에서 두 번째 질문은 본질적으로 await먹을 수있는 것에 관한 것입니다 .

어떤 종류의 방법을 사용할 수 await있습니까? 무효 반환 방법을 사용할 수 await있습니까?

아니요, 무효 반환 방법을 기다릴 수 없습니다. 컴파일러는 await M()에 대한 호출로 변환 되며 M().GetAwaiter(), 여기서 GetAwaiter인스턴스 메소드 또는 확장 메소드 일 수 있습니다. 기다리는 값은 기다리는 사람을 얻을 수있는 값이어야합니다. 분명히 void-returning 방법은 당신이 기다리는 사람을 얻을 수있는 가치를 생산하지 않습니다.

Task반환 방법은 대기 가능한 값을 생성 할 수 있습니다. 우리는 제 3자가 Task기다릴 수있는 유사한 객체 의 자체 구현을 만들고 싶어 할 것이며, 당신은 그것들을 기다릴 수있을 것입니다. 그러나 선언 허용되지 않습니다 async반환 아무것도하지만, 방법 void, Task또는 Task<T>.

(업데이트 : 마지막 문장은 C #의 미래 버전에 의해 위조 될 수 있습니다. 비동기 메서드에 대한 작업 유형 이외의 반환 유형을 허용하는 제안이 있습니다.)

(업데이트 : 위에서 언급 한 기능으로 인해 C # 7이되었습니다.)


7
+1 유일하게 누락 된 것은 void-returning async 메서드에서 예외를 처리하는 방법의 차이점입니다.
João Angelo

10
@JamesCadd : 일부 비동기 작업에서 예외가 발생한다고 가정합니다. 누가 잡아? 비동기 작업을 시작한 코드는 더 이상 스택에 없으며 동일한 스레드 에 없을 수도 있습니다. 예외는 모든 catch / finally 블록이 스택에 있다고 가정합니다 . 그래서 당신은 무엇을합니까? 예외 정보는 나중에 검사 할 수 있도록 작업에 저장됩니다. 그러나 메소드가 void를 리턴하면 사용자 코드에 사용 가능한 태스크가 없습니다. 우리가 그 상황을 정확히 다루는 방법은 몇 가지 논란의 문제였으며 지금은 우리가 결정한 것을 기억하지 않습니다.
Eric Lippert

8
실제로 BUILD에서 Stephen Toub에 대한이 질문을했습니다. .NET 4.0에서는 관찰되지 않은 TPL이 감지하면 작업에서 처리되지 않은 예외가 프로세스에서 충돌을 일으 킵니다. 4.5에서는 기본 동작이 변경되어 TaskScheduler :: UnobservedTaskException 이벤트를 통해 관찰되지 않은 예외가 계속보고되지만 더 이상 프로세스가 중단되지 않습니다. 이전 4.0 동작 을 원하면 <runtime> <ThrowUnobservedTaskExceptions enabled = "true"/> </ runtime>으로 다시 옵트 인 할 수 있습니다. 보이드 비동기 메소드의 화재를 잊어 버릴 수 있도록 정확하게 변경되었습니다.
Drew Marsh

4
async void메소드 SynchronizationContext는 실행을 시작할 때 활성화되었던 예외를 발생시킵니다 . 이것은 (동기식) 이벤트 핸들러의 동작과 유사합니다. @DrewMarsh : UnobservedTaskException및 런타임 설정은 메소드가 아니라 비동기 태스크 메소드 "실행 및 잊어 버림 "에만 적용됩니다 async void.
Stephen Cleary

1
비동기 예외 처리 정보를 인용 링크 : blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx#11
누가 복음 Puplett

23

발신자가 작업을 기다리거나 연속을 추가하려는 경우.

사실, 이벤트 핸들러를 작성하여 리턴 void수없는 경우 리턴해야하는 유일한 이유가 있습니다Task .


void 유형을 반환하는 메서드를 기다리는 것이 가능하다고 생각했습니다. 조금 더 자세히 설명해 주시겠습니까?
James Cadd

1
아냐, 못해 메소드가를 반환 void하면 생성 한 작업을 수행 할 방법이 없습니다. (실제로, 나는 심지어 그것을 생성하는지 확실하지 않습니다 Task)
SLaks

18

반환하는 메서드 TaskTask<T>구성 가능- 메서드 await내에서 async메서드 를 사용할 수 있음을 의미합니다 .

async리턴 void하는 메소드 는 합성 할 수 없지만 두 가지 중요한 특성이 있습니다.

  1. 이벤트 핸들러로 사용할 수 있습니다.
  2. "최상위"비동기 작업을 나타냅니다.

두 번째 요점은 뛰어난 비동기 작업 를 유지하는 컨텍스트를 처리 할 때 중요 합니다.

ASP.NET 컨텍스트는 그러한 컨텍스트 중 하나입니다. 비동기 Task메소드에서 기다리지 않고 비동기 메소드 를 사용 void하면 ASP.NET 요청이 너무 빨리 완료됩니다.

또 다른 맥락은 AsyncContext내가 단위 테스트를 위해 작성한 것입니다 ( 여기에서 사용 가능 )- AsyncContext.Run메소드는 미해결 작업 수를 추적하고 0 일 때 반환합니다.


12

유형 Task<T>은 태스크 병렬 라이브러리 (TPL)의 주력 유형이며, " T미래 에 유형 결과를 생성 할 일부 작업 / 작업"의 개념을 나타냅니다 . "미래에 완료되지만 결과를 반환하지 않는 작업"이라는 개념은 일반적인 작업 유형이 아닙니다.

정확하게 결과 유형 T이 어떻게 만들어 질 것인지는 특정 작업의 구현 세부 사항입니다. 작업은 로컬 시스템의 다른 프로세스, 다른 스레드 등으로 팜핑 될 수 있습니다. TPL 태스크는 일반적으로 현재 프로세스의 스레드 풀에서 작업자 스레드로 팜 아웃되지만 구현 세부 사항은 Task<T>유형의 기본이 아닙니다 . 오히려은 Task<T>을 생성하는 대기 시간이 긴 작업을 나타낼 수 있습니다 T.

위의 의견을 바탕으로 :

await표현식은 "이 표현식을 평가하여 나중에 결과를 생성 할 작업을 나타내는 오브젝트를 얻습니다. 해당 태스크의 지속과 연관된 콜백으로 현재 메소드의 나머지를 등록하십시오. 일단 태스크가 생성되고 콜백되면 가입하면 즉시 발신자에게 제어권을 반환합니다. " 이것은 일반적인 메소드 호출과는 대조적입니다. 즉, "하고있는 일을 기억하고, 완료 될 때까지이 메소드를 실행 한 다음 중단 된 위치를 선택하여 메소드의 결과를 알고 있습니다"라는 의미입니다.


편집 : 2011 년 10 월 MSDN Magazine에서 Eric Lippert의 기사를 인용해야합니다.

더 많은 정보와 화이트 페이지를 보려면 여기를 참조 하십시오 .

이것이 도움이되기를 바랍니다.

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