방프리

24.02.13 Chapter3. 태스크 기반 비동기 프로그래밍 (Item 31) 본문

C#/More Effective C#

24.02.13 Chapter3. 태스크 기반 비동기 프로그래밍 (Item 31)

방프리 2024. 2. 13. 21:26

Item 31 : 불필요한 콘텍스트 마셜링을 피하라

 

어떤 콘텍스트에서도 실행될 수 있는 코드를 '자유 코드' 라고 한다. 기본적으로 개발자가 작성하는 코드는 콘텍스트 자유 코드이다. 반대로 특정 SynchronizationContext에서만 실행될 수 있는 코드를 '콘텍스트 인식코드'라고 한다.
콘텍스트 인식코드는 주로 GUI, HTTPContext 등에 사용되며 이 콘텍스트들은 다른 콘텍스트와 스위칭 되며 실행되도록 해야한다. 콘텍스트를 전환해서 문제를 피하는 것이 콘텍스트를 전환하지 않아 발생하는 문제를 해결하는 것보다 더 나은 선택이기 때문이다.

이를 위해 ConfigureAwait() 메서드를 사용한다.

public static async Task<XElement> ReadPacket(string url)
{
    var result = await DownloadAsync(url)
    	.ConfigureAwait(continueOnCapturedContext: false);
    return XElement.Parse(result);
}

주의할 점은 ConfigureAwait() 을 사용한다고 해서 모든 동작이 기본 콘텍스트에서 실행되진 않는다.

public static async Task<Config> ReadConfig(string url)
{
    //하단 코드에서 비동기로 돌수도 있지만 동기적으로 돌 수도 있다.
    var result = await DownloadAsync(url)
        .ConfigureAwait(continueOnCapturedContext: false);
    var item = XElement.Parse(result);
    var userConfig = from node in items.Descendants()
                     where node.Name == "Config"
                     select node.Value;
    var configUrl = userConfig.SingleOrDefault();
    if (configUrl != null)
    {
    	//상단의 코드가 동기적으로 돌았다면 하단의 구문은
        //기본 콘텍스트가 아닌 캡쳐된 콘텍스트에서 동작할 수도 있다.
        result = await DownloadAsync(configUrl)
            .ConfigureAwait(continueOnCapturedContext: false);
        var config = await ParseConfig(result)
            .ConfigureAwait(continueOnCapturedContext: false);
        return config;
    }
    else
    {
        return new Config;
    }
}

 

private async void OnCommand(object sender, RoutedEventArgs e)
{
    var viewModel = (DataContext as SampleViewModel);
    try
    {
    	//해당 부분에서 ConfigureAwait()가 없기에 예기지 못하게 앱이 죽거나
        //효율적으로 콘텍스트가 동작하지 못함
        Config config = await ReadConfigAsync(viewModel);
        await viewModel.Update(config);
    }
    catch (Exception ex) when (logMessage(viewModel, ex))
    {
    }
}

public static async Task<Config> ReadConfig(string url)
{
    //하단 코드에서 비동기로 돌수도 있지만 동기적으로 돌 수도 있다.
    var result = await DownloadAsync(url)
        .ConfigureAwait(continueOnCapturedContext: false);
    var item = XElement.Parse(result);
    var userConfig = from node in items.Descendants()
                     where node.Name == "Config"
                     select node.Value;
    var configUrl = userConfig.SingleOrDefault();
    if (configUrl != null)
    {
    	//상단의 코드가 동기적으로 돌았다면 하단의 구문은
        //기본 콘텍스트가 아닌 캡쳐된 콘텍스트에서 동작할 수도 있다.
        result = await DownloadAsync(configUrl)
            .ConfigureAwait(continueOnCapturedContext: false);
        var config = await ParseConfig(result)
            .ConfigureAwait(continueOnCapturedContext: false);
        return config;
    }
    else
    {
        return new Config;
    }
}
Comments