以下代码未捕获通过调用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
以后能够等待在构造函数中启动的任务(并且为了使其与我的第二个示例相比,我使用了(async
)Window_Loaded
事件等待那里的方法,就像在previous question中向我建议的那样。然后我想提供一个选项来取消我在构造函数中启动的异步方法,我目前卡在那里因为异常处理不能按预期工作。
我的"非工作"代码,我可以通过将await Initialization
放在某个try-catch块中来捕获异常,但我仍然会得到一个额外的未处理异常。
我如何以允许我稍后等待我的异步方法的方式实现这一点(以确保我不使用我的对象的不一致状态)并且仍然能够取消长时间运行的任务(当然需要返回/设置默认值)?
答案 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
}
}