使用ContinueWith时如何捕获OperationCanceledException

Eri*_*and 5 c# asynchronous .net-4.0 async-await

我有一些代码,我将从.NET 4.5的可爱asyncawait关键字降级到.NET 4.0.我正在ContinueWith创建类似于await工作方式的延续.

基本上,我的旧代码是:

var tokenSource = newCancellationTokenSource();
var myTask = Task.Run(() =>
{
    return MyStaticClass.DoStuff(tokenSource.Token);
}, tokenSource.Token);
try
{
    var result = await myTask;
    DoStuffWith(result);
}
catch (OperationCanceledException)
{
    // Cancel gracefully.
}
Run Code Online (Sandbox Code Playgroud)

(正如人们所料,MyStaticClass.DoStuff(token)经常打电话token.ThrowIfCancellationRequested().)

我的新代码如下所示:

var tokenSource = new CancellationTokenSource();
try
{
    Task.Factory.StartNew(() =>
    {
        return MyStaticClass.DoStuff(tokenSource.Token);
    }, tokenSource.Token)
    .ContinueWith(task =>
    {
        var param = new object[1];
        param[0] = task.Result;
        // I need to use Invoke here because "DoStuffWith()" does UI stuff.
        Invoke(new MyDelegate(DoStuffWith, param));
    });
}
catch (OperationCanceledException)
{
    // Cancel gracefully.
}
Run Code Online (Sandbox Code Playgroud)

然而,OperationCanceledException从未被抓住.这是怎么回事?我在哪里放置try/catch块?

Luc*_*ski 8

取消的处理方式与其他例外情况不同.基本上,您可以使用此模式:

Task.Factory.StartNew(() =>
{
    // The task
}, tokenSource.Token)
.ContinueWith(task =>
{
    // The normal stuff
}, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(task =>
{
    // Handle cancellation
}, TaskContinuationOptions.OnlyOnCanceled)
.ContinueWith(task =>
{
    // Handle other exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
Run Code Online (Sandbox Code Playgroud)

或者替代方案:

Task.Factory.StartNew(() =>
{
    // The task
}, tokenSource.Token)
.ContinueWith(task =>
{
    switch (task.Status)
    {
    case TaskStatus.RanToCompletion:
        // The normal stuff
        break;
    case TaskStatus.Canceled:
        // Handle cancellation
        break;
    case TaskStatus.Faulted:
        // Handle other exceptions
        break;
    }
});
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你没有抓到任何东西,因为:

  • Task.Factory.StartNew 立即返回并始终成功.
  • 你的继续总是运行
  • 在任务被取消后访问task.ResultthrowsAggregateException
  • 任何事情都不会处理异常,因为它是从线程池线程抛出的.哎呀.接下来会发生什么取决于框架版本:

    • 在.NET <4.5中,一旦失败的任务完成,该过程将立即终止,因为您有一个未观察到的异常.
    • 在.NET> = 4.5中,将以静默方式删除该异常.