-
[C#] async/await 데드락 원인과 ConfigureAwait 정리Microsoft .NET/C# 2026. 11. 10. 00:55

UI 앱이나 레거시 코드에서
.Result·.Wait()를 쓰다가 화면이 멈추는 데드락, 그리고ConfigureAwait(false)가 필요한 이유입니다.절차
- 증상
- 원인: 동기 대기와 컨텍스트
- 해결: async all the way
- 라이브러리에서는 ConfigureAwait(false)
1. 증상
// WinForms/WPF 버튼 클릭 핸들러 var data = GetDataAsync().Result; // 여기서 멈춤(응답 없음)예외도 없이 그대로 멈춥니다. 디버거로 보면
WaitForSingleObject에서 대기 중입니다.2. 원인
await는 기본적으로 원래 SynchronizationContext(UI 스레드)로 돌아오려고 합니다. 그런데 UI 스레드는.Result에서 이미 블로킹 중이라 돌아갈 곳이 없어, 서로 기다리는 상태가 됩니다.3. 해결: async all the way
private async void OnLoad(object sender, EventArgs e) { var data = await GetDataAsync(); // 블로킹 없이 대기 label.Text = data; }이벤트 핸들러는
async void가 허용되는 거의 유일한 자리입니다. 그 외에는async Task를 씁니다.static async Task Main(string[] args) => await RunAsync();4. 라이브러리에서는 ConfigureAwait(false)
public async Task<string> ReadAsync() { var res = await _client.GetAsync(url).ConfigureAwait(false); return await res.Content.ReadAsStringAsync().ConfigureAwait(false); }컨텍스트가 필요 없는 코드에서는
ConfigureAwait(false)로 스레드 풀에서 이어가게 합니다. ASP.NET Core에는 SynchronizationContext가 없어 효과가 없지만, 공용 라이브러리라면 붙이는 편이 안전합니다.정리
.Result·.Wait()는 async 코드에서 쓰지 않는다- 진입점까지 async로 연결한다
- 라이브러리는
ConfigureAwait(false)
'Microsoft .NET > C#' 카테고리의 다른 글
[C#] DateTime vs DateTimeOffset, 서버 시간은 UTC로 관리하기 (0) 2026.11.10 [C#] HttpClient 재시도, IHttpClientFactory와 표준 리질리언스 정책 (0) 2026.11.10 [C#] record 타입 정리, class와 차이와 with 식 (0) 2026.11.10 [C#] TaskCanceledException과 OperationCanceledException, HttpClient 타임아웃 구분 (0) 2026.11.10 [C#] string 타입 형식지정 (0) 2021.10.15