방프리

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

C#/More Effective C#

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

방프리 2024. 1. 4. 23:31

Item 28 : async void 메서드는 절대 작성하지 말라

 

해당 챕터는 C#의 동시성 처리 관련 책을 읽었을 때 그 저자도 강조하던 멘트였다.
(Link : C# 동시성 프로그래밍 (Concurrency in C# Cookbook)
https://book.interpark.com/product/BookDisplay.do?_method=detail&sc.saNo=001&sc.prdNo=349595515&product2020=true)

그 이유는 async void는 대기할 수 없다. 즉, 앞 챕터에서 이야기 했던 async await의 장점을 살릴 수가 없다.
그럼에도 불가피하게 사용해야하는 경우가 있는데 바로 비동기 이벤트 핸들러를 만들 때이다.
async void를 사용하지 않아야 하는 이유 중 또 하나는 Task를 통해 대기할 수 없기 때문에 예외가 발생할 
경우 대처가 불가능하다. 그러므로 async void를 사용할 땐 절대 예외가 있어서는 안된다.

만약 예외가 발생하게 된다면 SynchronizationContext가 죽어버린다. 그러면 어떻게 해야될까?
예외를 외부로 던지도록하는 것이 좋다.

private async void OnCommand(object sender, RoutedEventArgs e)
{
    var viewModel = (DataContext as SampleViewModel);
    try
    {
        await viewModel.Update();
    }
    catch (Exception ex) when (logMessage(viewModel, ex))
    {
    }
}

private bool logMessage(SampleViewModel viewModel, Exception ex)
{
    viewModel.Messages.Add(ex.ToString());
    return false;
}

public static class Utilities
{
    public static async void FireAndForget(this Task,
        Action<Exception> onErrors)
    {
        try
        {
            await task;
        }
        catch (Exception ex)
        {
            onErrors(ex);
        }
    }
    
    public static async void FireAndForget(this Task task,
        Func<Exception, bool> onoError)
    {
        try
        {
            await task;
        }
        catch (Exception ex) when (onError(ex))
        {
        }
    }
}

//만약 특정 예외 타입에 대처할 수 없는 가정이라면
//특정 예외타입을 제네릭 타입으로 만들자
public static async void FireAndForget<TException>
    (this Task task,
    Action<TException> recovery,
    Func<Exception, bool> onError)
    where TException : Exception
{
    try
    {
        await task;
    }
    catch (Exception ex) when (onError(ex))
    {
    }
    catch (TException ex2)
    {
        recovery(ex2);
    }
}

다시 강조하지만 async void에서 예외가 발생했을 땐 어쩔 수 없이 프로그램이 중단되어야 한다. 하지만 그 중단을 
최대한 안전한 방향으로 이끄는 방법밖에 대처할 수 없다. 그러므로 왠만하면 사용하지 말자!

Comments