C# async / await unobserved exception

2020-04-11 09:45发布

I'm trying to understand why the following code:

async void Handle_Clicked(object sender, System.EventArgs e)
{
    try
    {
        await CrashAsync("aaa");
    }
    catch (Exception exception)
    {
        Log($"observed exception");

        Log($"Exception: {exception.Message}");
    }
}

private async Task CrashAsync(string title)
{
    Log($"CrashAsync ({title}) - before");

    await Task.Delay(1000);

    throw new Exception($"CrashAsync ({title})");

    Log($"CrashAsync ({title}) - after");
}

produces the expected result:

thread #1: CrashAsync (aaa) - before

thread #1: observed exception

thread #1: Exception: CrashAsync (aaa)

but if I change it to this one:

async void Handle_Clicked(object sender, System.EventArgs e)
{
    try
    {
        await CrashAsync("aaa").ContinueWith(async (t) =>
        {                   
            await CrashAsync("bbb");
        },TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    catch (Exception exception)
    {
        Log($"observed exception");

        Log($"Exception: {exception.Message}");
    }
}

I get the following output:

thread #1: CrashAsync (aaa) - before

thread #1: observed exception

thread #1: Exception: A task was canceled.

thread #2: unobserved exception

thread #2: System.Exception: CrashAsync (aaa) at AsyncTest.AsyncTestPage+c__async3.MoveNext () [0x000ad] in /Users/johndoe/Development/Xamarin/AsyncTest/AsyncTest/AsyncTestPage.xaml.cs:82

where:

TaskScheduler.UnobservedTaskException += (sender, e) =>
{
    Debug.WriteLine($"thread #{Environment.CurrentManagedThreadId}: unobserved exception");

    foreach (var exception in e.Exception.Flatten().InnerExceptions)
    {
        Debug.WriteLine($"thread #{Environment.CurrentManagedThreadId}: {exception}");
    }
};

The continuation condition is not satisfied so the ContinueWith task is cancelled, but why do I have unobserved exception?

1条回答
够拽才男人
2楼-- · 2020-04-11 10:26

You await Task returned by ContinueWith, so you observe exception related to this Task - that it was cancelled (TaskCanceledException). But you don't observe original exception thrown by CrashAsync (so "CrashAsync aaa") exception, hence the behavior you observe.

Here is sample code to get more understanding:

static async void Test() {
    var originalTask = CrashAsync("aaa");
    var onSuccess = originalTask.ContinueWith(async (t) =>
    {
        await CrashAsync("bbb");
    }, TaskContinuationOptions.OnlyOnRanToCompletion);
    var onFault = originalTask.ContinueWith(t => {                    
        Log("Observed original exception: " + t.Exception.InnerExceptions[0].Message);
    }, TaskContinuationOptions.OnlyOnFaulted);
}

So in short - just await your task and catch exception if any. You don't need to use ContinueWith at all, because if you use await - the rest of the method is already a continuation.

查看更多
登录 后发表回答