Programing

try / catch / finally에서 await에 대한 좋은 솔루션입니까?

lottogame 2020. 9. 6. 11:49
반응형

try / catch / finally에서 await에 대한 좋은 솔루션입니까?


다음 과 같이 예외 (스택 추적 포함)를 다시 던지기 전에 블록 async에서 메서드 를 호출해야합니다 catch.

try
{
    // Do something
}
catch
{
    // <- Clean things here with async methods
    throw;
}

그러나 불행히도 당신은 또는 블록 await에서 사용할 수 없습니다 . 컴파일러가 명령이나 그와 비슷한 것을 실행하기 위해 블록으로 돌아갈 방법이 없기 때문 입니다.catchfinallycatchawait

Task.Wait()교체 에 사용하려고했는데 await교착 상태가 발생했습니다. 나는 이것을 피할 수있는 방법을 웹에서 검색 하고이 사이트를 찾았다 .

async메서드를 변경할 수 없고을 사용하는지 알 수 없기 때문에 다른 스레드 (교착 상태를 피하기 위해)에있을 때 비동기 메서드를 시작하고 완료를 기다리는 ConfigureAwait(false)a Func<Task>사용하는 다음 메서드를 만들었습니다 .

public static void AwaitTaskSync(Func<Task> action)
{
    Task.Run(async () => await action().ConfigureAwait(false)).Wait();
}

public static TResult AwaitTaskSync<TResult>(Func<Task<TResult>> action)
{
    return Task.Run(async () => await action().ConfigureAwait(false)).Result;
}

public static void AwaitSync(Func<IAsyncAction> action)
{
    AwaitTaskSync(() => action().AsTask());
}

public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action)
{
    return AwaitTaskSync(() => action().AsTask());
}

제 질문은 :이 코드가 괜찮다고 생각하십니까?

물론 개선 사항이 있거나 더 나은 접근 방식을 알고 있다면 듣고 있습니다! :)


를 사용하여 논리를 catch블록 외부로 이동하고 필요한 경우 예외를 다시 throw 할 수 있습니다 ExceptionDispatchInfo.

static async Task f()
{
    ExceptionDispatchInfo capturedException = null;
    try
    {
        await TaskThatFails();
    }
    catch (MyException ex)
    {
        capturedException = ExceptionDispatchInfo.Capture(ex);
    }

    if (capturedException != null)
    {
        await ExceptionHandler();

        capturedException.Throw();
    }
}

이렇게하면 호출자가 예외의 StackTrace속성을 검사 할 때 예외가 발생한 위치를 기록 TaskThatFails합니다.


C # 6.0부터 awaitin catchfinally블록 을 사용할 수 있으므로 실제로 다음과 같이 할 수 있습니다.

try
{
    // Do something
}
catch (Exception ex)
{
    await DoCleanupAsync();
    throw;
}

The new C# 6.0 features, including the one I just mentioned are listed here or as a video here.


If you need to use async error handlers, I'd recommend something like this:

Exception exception = null;
try
{
  ...
}
catch (Exception ex)
{
  exception = ex;
}

if (exception != null)
{
  ...
}

The problem with synchronously blocking on async code (regardless of what thread it's running on) is that you're synchronously blocking. In most scenarios, it's better to use await.

Update: Since you need to rethrow, you can use ExceptionDispatchInfo.


We extracted hvd's great answer to the following reusable utility class in our project:

public static class TryWithAwaitInCatch
{
    public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync,
        Func<Exception, Task<bool>> errorHandlerAsync)
    {
        ExceptionDispatchInfo capturedException = null;
        try
        {
            await actionAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            capturedException = ExceptionDispatchInfo.Capture(ex);
        }

        if (capturedException != null)
        {
            bool needsThrow = await errorHandlerAsync(capturedException.SourceException).ConfigureAwait(false);
            if (needsThrow)
            {
                capturedException.Throw();
            }
        }
    }
}

One would use it as follows:

    public async Task OnDoSomething()
    {
        await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync(
            async () => await DoSomethingAsync(),
            async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; }
        );
    }

Feel free to improve the naming, we kept it intentionally verbose. Note that there is no need to capture the context inside the wrapper as it is already captured in the call site, hence ConfigureAwait(false).

참고URL : https://stackoverflow.com/questions/16626161/a-good-solution-for-await-in-try-catch-finally

반응형