如何使用 ContinueWith 正确管理任务中的异常

Posted

技术标签:

【中文标题】如何使用 ContinueWith 正确管理任务中的异常【英文标题】:How to manage properly an exception in a Task with ContinueWith 【发布时间】:2013-09-21 16:44:14 【问题描述】:

在阅读了有关任务和执行管理的信息后,我正在使用此代码来管理任务中引发的异常:

Task<Object> myTask = Task.Factory.StartNew<Object>(doTask, CancellationToken.None,   TaskCreationOptions.None, TaskScheduler.Default);
myTask .ContinueWith(task => afterTask(task), TaskScheduler.FromCurrentSynchronizationContext());

doTask 和 AfterTask 在哪里:

private <Object> doTask() 
    throw new Exception("BOOM");


private afterTask(Task<Object> aTask) 

        if (aTask.IsFaulted)
        
            MessageBox.Show(aTask.Exception.InnerException.Message);
        
        else //whatever

当抛出 Exception Boom 时,Visual Studio 会显示一条警报,通知尚未捕获到异常,但如果我继续执行,则会在 afterTask 函数中处理异常。

这段代码是正确的还是我误解了任务的一些基本行为?有什么方法可以避免调试器发出未捕获到执行的警报?是不是有点烦……

提前致谢

【问题讨论】:

【参考方案1】:

试试这个:

 task.ContinueWith(
            t =>
            t.Exception.Handle(ex =>
                                   
                                       logger.Error(ex.Message, ex);
                                       return false;
                                   )

            , TaskContinuationOptions.OnlyOnFaulted
            );

通过使用TaskContinuationOptions.OnlyOnFaulted,您仅在原始任务引发异常时才运行ContinueWith 块。

此外,您可以选择是否从传递给Handle 的 lambda 中返回 true 或 false,指示是否已处理异常。就我而言,我不想阻止异常传播。在您的情况下,您可能希望将其更改为返回 true

【讨论】:

非常感谢您的超级快速回答。尽管代码可以工作,但它并不能阻止调试器在抛出 BOOM 时警告异常没有被捕获。使用该代码而不是我发布的代码有什么好处吗? 在 Visual Studio Debug->Exceptions-> 取消选中 commonlanguageruntimeexceptions throwed 复选框点击确定 主要优点是ContinueWith 任务只会在抛出异常时运行。在您发布的示例中,该任务将始终运行。第二个优点是可读性。为了防止那些 VS 弹出窗口,我会试试这个:***.com/questions/2173997/…【参考方案2】:
        try
        
            var t1 = Task.Delay(1000);

            var t2 = t1.ContinueWith(t =>
            
                Console.WriteLine("task 2");
                throw new Exception("task 2 error");
            , TaskContinuationOptions.OnlyOnRanToCompletion);

            var t3 = t2.ContinueWith(_ =>
            
                Console.WriteLine("task 3");
                return Task.Delay(1000);
            , TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap();

            // The key is to await for ALL tasks rather than just
            // the first or last task.
            await Task.WhenAll(t1, t2, t3);
        
        catch (AggregateException aex)
        
            aex.Flatten().Handle(ex =>
                
                    // handle your exceptions here
                    Console.WriteLine(ex.Message);
                    return true;
                );
        

【讨论】:

以上是关于如何使用 ContinueWith 正确管理任务中的异常的主要内容,如果未能解决你的问题,请参考以下文章

[C#]Task.ContinueWith使用实例

[C#]Task.ContinueWith使用实例

使用 SemaphoreSlim 和 Continuewith 下载 URL

组合ContinueWith

在任务中捕获异常的最佳方法是啥?

Task_等待任务完成几种方式