728x90
반응형

이번 포스팅은 기존 프로젝트를 리팩토링 하면서 알게 된
Task.ContinueWith 메서드를 분석하기 위해 작성했습니다.
| 코드 예제
먼저 같은 기능을 async/await 방식과 Task.ContinueWith 방식으로 구현했습니다.
| async/await 방식
public async Task<string> GetDataAsync()
{
var data = await Task.Run(() =>
{
Thread.Sleep(1000); // 긴 작업
return "완료됨";
});
return $"결과: {data}";
}
| ContinueWith 방식
public Task<string> GetDataWithContinueAsync()
{
return Task.Run(() =>
{
Thread.Sleep(1000); // 긴 작업
return "완료됨";
}).ContinueWith(task =>
{
return $"결과: {task.Result}";
});
}
위 두 개의 예제 코드는 기능적으로 유사하지만 코드의 구조와 읽기 쉬움에 차이가 있습니다.
| 비교: await vs ContinueWith
| 항목 | await | ContinueWith |
| 가독성 | 매우 높음, 동기 코드처럼 읽힘 | 중첩되기 쉬움, 콜백 지옥 가능성 |
| 예외 처리 | try-catch로 자연스럽게 처리 | .Exception, .IsFaulted로 별도 처리 필요 |
| 동기화 컨텍스트 | 기본적으로 원래 컨텍스트로 복귀 | 기본적으로 백그라운드 스레드에서 실행 |
| 디버깅 | 흐름 파악이 쉬움 | 디버깅 시 콜백 지점 추적 어려움 |
| 유연성 | 깔끔한 흐름 제어 | 병렬 처리 조합 시 유리할 수 있음 |
await와 ContinueWith의 가장 큰 차이점이라 생각되는 것은
예외 처리 부분입니다.
| 예외 처리
| await 예외 처리
try
{
string result = await GetDataAsync();
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine($"예외 발생: {ex.Message}");
}
| ContinueWith 예외 처리
GetDataWithContinueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine($"예외 발생: {task.Exception?.GetBaseException().Message}");
}
else
{
Console.WriteLine(task.Result);
}
});
ContinueWith에서는 task.IsFaulted를 수동으로 확인하고
task.Exception을 통해 예외를 처리해야 하므로 코드가 복잡해집니다.
그럼에도 불구하고 ContinueWith을 사용할 때가 있습니다.
| await 장점
- 가독성 우수: 동기 코드처럼 읽히기 때문에 유지보수가 쉽고, 협업 시 이해하기 쉬움.
- 예외 처리 편리: try-catch를 통해 자연스럽게 비동기 예외를 처리 가능.
- 동기화 컨텍스트 자동 복귀: UI 스레드에서 호출 시, 자동으로 UI 컨텍스트로 복귀 (Windows Forms, WPF 등에서 유리).
- 디버깅이 쉬움: 흐름이 선형적이라 디버깅 도구로 추적이 쉬움.
- 표준 방식: .NET의 최신 비동기 코드 스타일과 일치, 권장 방식.
| ContinueWith 장점
- 제어 흐름 유연: 여러 작업을 조건적으로 연결하거나 복잡한 체인을 구성할 때 유리.
- 병렬 처리와 조합 용이: Task.WhenAll, Task.WhenAny, ContinueWhenAll 등과 조합하면 높은 유연성을 가짐.
- 명시적 실행 컨텍스트 지정 가능: TaskScheduler를 통해 명확히 실행 컨텍스트 지정 가능.
| 유의점
- ContinueWith는 기본적으로 현재 동기화 컨텍스트를 무시하고 새 스레드에서 실행됩니다.
UI 쓰레드에서 UI 작업을 하려면 TaskScheduler.FromCurrentSynchronizationContext()를 명시적으로 사용해야 합니다. - await는 기본적으로 호출한 스레드의 컨텍스트를 캡처하고, 해당 컨텍스트에서 이어 실행합니다.
단, .ConfigureAwait(false)를 사용하면 캡처하지 않습니다. - ContinueWith는 복잡한 작업 흐름을 정의하거나 WhenAll, WhenAny 등과 함께 사용할 때 유용할 수 있으나,
대부분의 경우 await가 더 직관적이고 안전합니다.
| 마무리
정리하면서 느꼈지만 그냥 await/async를 쓰는 게 더 좋아 보이네요 ㅎㅎ
질문이나 틀린 부분이 있으면 댓글로 남겨주세요!
728x90
반응형
'C#' 카테고리의 다른 글
| C# 비동기 프로그래밍 : Action / event 차이 (4) | 2025.07.29 |
|---|---|
| C# CancellationToken 연동(연결) 방법 - CreateLinkedTokenSource (1) | 2025.07.23 |
| C# First/FirstOrDefualt, Last/LastOrDefualt 차이 (0) | 2025.05.28 |
| C# 정규 표현식(Regular Expression) 활용 (Match, Result) (0) | 2024.10.27 |
| C# 정규 표현식(Regular Expression) 이해 (0) | 2024.10.26 |