异步方法中的异常未被捕获

时间:2015-02-20 12:49:34

标签: c# asynchronous cancellation

以下代码未捕获通过调用ct.ThrowIfCancellationRequested引发的OperationCancelException。

public partial class TitleWindow : Window, IAsyncInitialization
{
    public Task Initialization{get; private set;}
    CancellationTokenSource cts;

    public TitleWindow()
    {
        InitializeComponent();
        cts = new CancellationTokenSource();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        try
        {
            cts.Cancel();
            Initialization = GetCancelExceptionAsync(cts.Token);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation canceled!");
        }
    }

    public async Task GetCancelExceptionAsync(CancellationToken ct)
    {
        await Task.Delay(1000);
        ct.ThrowIfCancellationRequested();
    }
}

但是,如果我用以下方法替换Window_Loaded方法(使其异步并等待我的异步方法的调用),则会捕获异常。

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    try
    {
        cts.Cancel();
        await GetCancelExceptionAsync(cts.Token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Operation canceled!");
    }
}

为什么我的第一种方法不起作用?异常是否未正确传播到正确的同步上下文?

我试图使用Stephen Clearys blog post中描述的The Asynchronous Initialization Pattern以后能够等待在构造函数中启动的任务(并且为了使其与我的第二个示例相比,我使用了(asyncWindow_Loaded事件等待那里的方法,就像在previous question中向我建议的那样。然后我想提供一个选项来取消我在构造函数中启动的异步方法,我目前卡在那里因为异常处理不能按预期工作。

我的"非工作"代码,我可以通过将await Initialization放在某个try-catch块中来捕获异常,但我仍然会得到一个额外的未处理异常。

我如何以允许我稍后等待我的异步方法的方式实现这一点(以确保我不使用我的对象的不一致状态)并且仍然能够取消长时间运行的任务(当然需要返回/设置默认值)?

2 个答案:

答案 0 :(得分:2)

在第一个示例中,未捕获异常,因为它在离开try/catch块之前不会发生。如果你想在那里捕获它,你需要等待/ await,就像你在第二个例子中那样。 如果你没有等待返回的任务,那么该方法将继续执行,并在异常实际发生之前离开try/catch块...

如果你想捕获“带外”异常,你也可以注册到TaskScheduler.UnobservedTaskException(如果一个任务正在抛出一个无法捕获的异常,则调用此事件)以获取所有未捕获的异常或监视任务Exception属性。也可以查看THIS回答。

答案 1 :(得分:1)

另一个线程上的任务抛出异常。

public async Task GetCancelExceptionAsync(CancellationToken ct)
        {
            try
            {
                await Task.Delay(1000);
                ct.ThrowIfCancellationRequested();
            }
            catch (Exception e)
            {
                // your Cancleation expeption
            }
        }