Console/Service Application을 위한 SynchronizationContext - AsyncContext
일반적으로 WPF/WinForm 프로그램을 하면서 SynchronizationContext를 접하게 되는데요, 사실 마이크로소프트가 제공해주는 기본 클래스에 불과하고 WPF는 DispatcherSynchronizationContext로, WinForm은 WindowsFormsSynchronizationContext로 하위 클래스를 정의하는 방식입니다.
즉, Console 응용 프로그램이나 NT Service 프로그램을 위해서 정의할 수 있지만 일단은 마이크로소프트에서 그런 환경을 위한 SynchronizationContext는 만들지 않았을 뿐입니다. 그렇다면 당연히, 만들면 됩니다. ^^ 그리고 실제로 그걸 다음의 라이브러리에서 제공하고 있습니다.
StephenCleary / AsyncEx
; https://github.com/StephenCleary/AsyncEx
Install-Package Nito.AsyncEx -Version 5.0.0
위의 라이브러리에서 SynchronizationContext를 구현한 타입은 AsyncContext인데,
AsyncContext
; https://github.com/StephenCleary/AsyncEx/wiki/AsyncContext
링크에서 설명한 바와 같이 AsyncContext.Run을 통해 실행한 이후부터는,
class Program
{
static async Task<int> AsyncMain()
{
// ... 이 내부에서 SynchronizationContext가 제공됨
}
static int Main(string[] args)
{
return AsyncContext.Run(AsyncMain);
}
}
SynchronizationContext가 해당 스레드 문맥에 제공되므로 Post/Send 메서드로 SynchronizationContext의 기능을 수행할 수 있습니다.
그런데, 사실 Console 응용 프로그램 등에서 저걸 써야 할 상황이 얼마나 있을까? 하는 의문이 듭니다. 만약 써야 한다면, 아마도 멋들어진 CUI를 구현해 그것을 UI로 두고 동기화를 지키는 용도로 쓰면 될 테지만... 글쎄요, 분명 활용도가 많지 않아 보입니다. (마이크로소프트가 만들어 두지 않은 이유일 것입니다.)
그래도 저 같은 사람한테는 편한 용도가 하나 있긴 합니다. 가령, 다음과 같은 글을 쓰면서,
WebClient 타입의 ...Async 메서드 호출은 왜 await + 동기 호출 시 hang 현상이 발생할까요?
; https://www.sysnet.pe.kr/2/0/11419
재현 코드를 만들기 위해 프로젝트 유형을 Windows Forms로 했는데, 이제는 그냥 AsyncEx를 참조해 콘솔 응용 프로그램으로도 간단한 재현 프로그램을 만들 수 있게 되었습니다. ^^ 실제로 다음의 코드는 "
WebClient 타입의 ...Async 메서드 호출은 왜 await + 동기 호출 시 hang 현상이 발생할까요?" 글에 실은 예제와 정확히 동일한 hang 현상을 겪습니다.
using Nito.AsyncEx;
using System;
using System.Net;
using System.Threading.Tasks;
namespace ConsoleApp1
{
// Install-Package Nito.AsyncEx -Version 5.0.0
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(AsyncMain);
}
static async Task<int> AsyncMain()
{
Uri uri = new Uri("https://www.naver.com");
Task<string> textTask = GetHtmlTextAsync(uri);
string result = textTask.Result; // hang 현상 발생함.
return 0;
}
public static async Task<string> GetHtmlTextAsync(Uri uri)
{
var client = new WebClient();
{
string result = await client.DownloadStringTaskAsync(uri).ConfigureAwait(false);
return result;
}
}
}
}
뭐... 딱 그 정도... 활용 사례입니다. ^^;
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]