가장 간단한 형태로 비동기 대기를 이해하려고합니다. 이 예제를 위해 두 개의 숫자를 더하는 매우 간단한 방법을 만들고 싶습니다. 처리 시간이 전혀 없으며 여기서 예제를 작성하는 것입니다.
실시 예 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
실시 예 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
기다리는 경우 DoWork1Async()
코드가 동기식 또는 비동기식으로 실행됩니까?
Task.Run
UI 스레드를 차단하지 않도록 메소드를 대기 가능하고 비동기 적으로 만들 려면 동기화 코드를 래핑해야 합니까?
내 메서드가 Task
반환인지 또는 반환 인지 알아 내려고 노력 중 입니다. 비 동기화하기 위해 Task<T>
코드를 래핑해야합니까?Task.Run
어리석은 질문 확실하지만 사람들이 내부에 비동기가없고 Task.Run
또는에 싸여 있지 않은 코드를 기다리는 곳의 예를 봅니다 StartNew
.
답변
먼저, “비동기”( async
) 라는 용어를 정리해 보자 . 에서 async
방법, 그 “수율”포인트는 await
표현은.
이것은 “백그라운드 스레드에서 실행”을 의미하기 위해 MSDN 문서에서 수년 동안 사용 된 “비동기”라는 용어와는 매우 다릅니다.
추가 async
로이 문제를 혼동시키기 위해 “대기 가능” 과 는 매우 다릅니다. async
반환 유형이 대기 할 수없는 일부 메소드와 대기하지 않는 유형을 리턴하는 많은 메소드가 있습니다 async
.
그들이 아닌 것에 대해 충분히 ; 여기에 그들이 무엇 있습니다 :
async
키워드는 (즉, 그것은 수있는 비동기 방식을 허용await
표현).async
방법은 반환 할 수 있습니다Task
,Task<T>
(당신이해야하는 경우), 또는void
.- 특정 패턴을 따르는 모든 유형을 기다릴 수 있습니다. 가장 일반적인 대기 유형은
Task
및Task<T>
입니다.
따라서 귀하의 질문을 ” 대기 가능한 방식으로 백그라운드 스레드 에서 작업 을 어떻게 실행할 수 있습니까?”로 재구성하면 대답은 다음과 Task.Run
같습니다.
private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}
그러나이 패턴은 좋지 않은 접근 방식입니다 (아래 참조).
그러나 귀하의 질문이 ” async
차단하는 대신 호출자에게 되돌릴 수 있는 메소드를 작성하는 방법”인 경우, 메소드를 선언 하고 “수확”지점에 async
사용 하는 것이 답입니다 await
.
private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}
따라서 기본적인 것의 패턴은 async
코드가 await
표현의 “대기 가능”에 의존 하도록하는 것입니다 . 이러한 “대기 가능 항목”은 다른 async
방법이거나 대기 가능 항목을 반환하는 일반적인 방법 일 수 있습니다 . 반환 정기 방법은 Task
/ Task<T>
수 사용하는 Task.Run
백그라운드 스레드에서 코드를 실행하거나 (더 일반적으로) 그들이 사용할 수 있습니다 TaskCompletionSource<T>
또는 바로 가기 (하나 TaskFactory.FromAsync
, Task.FromResult
등). 나는 하지 않는 전체 방법을 포장하는 것이 좋습니다 Task.Run
; 동기 메소드는 동기 서명을 가져야하며, 다음과 같이 랩핑되어야하는지 소비자에게 맡겨야합니다 Task.Run
.
private int DoWork()
{
return 1 + 2;
}
private void MoreSynchronousProcessing()
{
// Execute it directly (synchronously), since we are also a synchronous method.
var result = DoWork();
...
}
private async Task DoVariousThingsFromTheUIThreadAsync()
{
// I have a bunch of async work to do, and I am executed on the UI thread.
var result = await Task.Run(() => DoWork());
...
}
나는이 async
/ await
소개 내 블로그를; 마지막에는 좋은 후속 리소스가 있습니다. MSDN 문서 async
도 비정상적으로 좋습니다.
답변
와 방법을 장식 할 때 기억해야 할 가장 중요한 일 중 하나는 비동기은 적어도 있다는 것입니다 하나의 await를 메소드 내부 연산자. 귀하의 예에서는 TaskCompletionSource를 사용하여 아래에 표시된 것처럼 번역합니다 .
private Task<int> DoWorkAsync()
{
//create a task completion source
//the type of the result value must be the same
//as the type in the returning Task
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
Task.Run(() =>
{
int result = 1 + 2;
//set the result to TaskCompletionSource
tcs.SetResult(result);
});
//return the Task
return tcs.Task;
}
private async void DoWork()
{
int result = await DoWorkAsync();
}
답변
Task.Run을 사용하여 메서드를 실행하면 Task가 스레드 풀에서 스레드를 가져와 해당 메서드를 실행합니다. UI 스레드의 관점에서 볼 때 UI 스레드를 차단하지 않으므로 “비동기”입니다. 일반적으로 사용자 상호 작용을 처리하기 위해 많은 스레드가 필요하지 않으므로 데스크톱 응용 프로그램에 적합합니다.
그러나 웹 응용 프로그램의 경우 각 요청은 스레드 풀 스레드에 의해 서비스되므로 이러한 스레드를 저장하여 활성 요청 수를 늘릴 수 있습니다. 비동기 작업을 시뮬레이션하기 위해 스레드 풀 스레드를 자주 사용하는 것은 웹 응용 프로그램에서 확장 할 수 없습니다.
True Async는 파일 / DB 액세스 등과 같은 I / O 작업에 스레드를 반드시 사용해야하는 것은 아닙니다. I / O 작업에 스레드가 필요하지 않은 이유를 이해하려면이 내용을 읽을 수 있습니다. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
간단한 예제에서는 순수한 CPU 바운드 계산이므로 Task.Run을 사용하는 것이 좋습니다.