C# Asynchronous(비동기)
Asynchronous (비동기)
C# 에 비동기에 대해서 알아 보도록 하자.
Async / Await 의 Best Practice
1. Async Void는 피하자.
비동기 메서드는 Task, Task<T>, Void의 반환 형식이 있지만, Void 반환 형식은 EventHandler를 제외 하고는 사용하지 않는다.
Void 반환의 예외처리는 Task, Task<T>와 다르다.
private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here!
throw;
}
}
Task 반환 형식의 비동기 호출은 예외가 발생하면 캡쳐 해서 Task에 할당 한다.
하지만 Task가 없는 Void 반환 형식은 void 메서드가 시작될 때 활성화 된 SynchronizationContext 에서 직접 발생 한다.
그래서 호출한 Void Method에서는 Catch 할 수 없는 것이다.
그렇지만 우리는 분명히 Async Void를 사용할 일이 있다. EventHander는 대부분 Void이기 때문이다.
그렇다면 Exception Catch를 희생하지 않고 어떻게 실행 할 수 있을까?
Async Void Method 에서 Exception Catch
private async void button1_Click(object sender, EventArgs e)
{
await Button1ClickAsync(); // await 키워드를 사용 한다.
}
public async Task Button1ClickAsync() //Task를 반환 한다.
{
// Do asynchronous work.
await Task.Delay(1000);
}
다시한번 확인 하지만, 우리는 EventHandler를 제외 하고 모든 비동기 처리는
Task 반환 타입으로 구성해야 합니다.
이유는 위에서 확인 했듯이, 오류 처리, 구성 가능성 및 테스트 가능성이 더 쉬워지기 때문입니다.(선택은 자유 ^^)
2. 동기 코드와 비동기 코드 혼합 사용 주의
먼저 아래의 예제를 살펴 보자.
일반적인 교착 상태 문제
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000); //Work Thread가 UI Thread를 대기
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait();
}
}
ConfigureAwait의 사용하여 교착상태 문제 해결
async Task MyMethodAsync()
{
// Code here runs in the original context.
await Task.Delay(1000);
// Code here runs in the original context.
await Task.Delay(1000).ConfigureAwait(
continueOnCapturedContext: false);
// Code here runs without the original
// context (in this case, on the thread pool).
}
continueOnCapturedContext가 true인 경우는 Capture한 Context를 Task 완료 후 계속해서 사용 하겠다는 의미이다. false면 Thread Pool에서 새롭게 할당 받아서 다음 코드로 진행 된다. 자연스럽게 교착상태를 피할 수 있다.
하지만 Task가 완료 된 후 UI Context를 사용해야 한다면, ConfigureAwait()는 사용할 수 없다.
그래서 아래와 같이 사용 해야 한다.
private async Task HandleClickAsync()
{
// Can use ConfigureAwait here.
await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
}
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
// Can't use ConfigureAwait here.
await HandleClickAsync();
}
finally
{
// We are back on the original context for this method.
button1.Enabled = true;
}
}
댓글남기기