为了账号安全,请及时绑定邮箱和手机立即绑定

在Task中捕获异常的最佳方法是什么?

在Task中捕获异常的最佳方法是什么?

人到中年有点甜 2019-10-17 14:26:00
使用System.Threading.Tasks.Task<TResult>,我必须管理可能引发的异常。我正在寻找做到这一点的最佳方法。到目前为止,我已经创建了一个基类,该基类在调用时管理所有未捕获的异常。.ContinueWith(...)我想知道是否有更好的方法可以做到这一点。甚至是这样做的好方法。public class BaseClass{    protected void ExecuteIfTaskIsNotFaulted<T>(Task<T> e, Action action)    {        if (!e.IsFaulted) { action(); }        else        {            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>            {                /* I display a window explaining the error in the GUI                  * and I log the error.                 */                this.Handle.Error(e.Exception);            }));                    }    }}   public class ChildClass : BaseClass{    public void DoItInAThread()    {        var context = TaskScheduler.FromCurrentSynchronizationContext();        Task.Factory.StartNew<StateObject>(() => this.Action())                    .ContinueWith(e => this.ContinuedAction(e), context);    }    private void ContinuedAction(Task<StateObject> e)    {        this.ExecuteIfTaskIsNotFaulted(e, () =>        {            /* The action to execute              * I do stuff with e.Result             */        });            }}
查看完整描述

2 回答

?
POPMUISE

TA贡献1765条经验 获得超5个赞

有两种方法可以执行此操作,具体取决于您所使用的语言版本。


C#5.0以上

您可以使用async和await关键字为您简化很多操作。


async并且await被引入该语言以简化使用Task Parallel Library的工作,从而避免了您必须使用ContinueWith并允许您以自上而下的方式继续编程。


因此,您可以简单地使用try/catch块来捕获异常,如下所示:


try

{

    // Start the task.

    var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });


    // Await the task.

    await task;

}

catch (Exception e)

{

    // Perform cleanup here.

}

请注意,封装上述内容的方法必须使用已async应用关键字,这样您才可以使用await。


C#4.0及以下

您可以使用从枚举中获取值的ContinueWith重载来处理异常,如下所示:TaskContinuationOptions


// Get the task.

var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });


// For error handling.

task.ContinueWith(t => { /* error handling */ }, context,

    TaskContinuationOptions.OnlyOnFaulted);

在OnlyOnFaulted该成员TaskContinuationOptions枚举指示应继续只当先行任务抛出异常执行。


当然,您可以有多个调用来ContinueWith取消同一先决条件,从而处理非例外情况:


// Get the task.

var task = new Task<StateObject>(() => { /* action */ });


// For error handling.

task.ContinueWith(t => { /* error handling */ }, context, 

    TaskContinuationOptions.OnlyOnFaulted);


// If it succeeded.

task.ContinueWith(t => { /* on success */ }, context,

    TaskContinuationOptions.OnlyOnRanToCompletion);


// Run task.

task.Start();


查看完整回答
反对 回复 2019-10-17
?
慕慕森

TA贡献1856条经验 获得超17个赞

您可以创建一些自定义的Task工厂,该工厂将生成嵌入了异常处理过程的Task。像这样:


using System;

using System.Threading.Tasks;


class FaFTaskFactory

{

    public static Task StartNew(Action action)

    {

        return Task.Factory.StartNew(action).ContinueWith(

            c =>

            {

                AggregateException exception = c.Exception;


                // Your Exception Handling Code

            },

            TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously

        ).ContinueWith(

            c =>

            {

                // Your task accomplishing Code

            },

            TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously

        );

    }


    public static Task StartNew(Action action, Action<Task> exception_handler, Action<Task> completion_handler)

    {

        return Task.Factory.StartNew(action).ContinueWith(

            exception_handler,

            TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously

        ).ContinueWith(

            completion_handler,

            TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously

        );

    }

};

您可以在客户端代码中忘记从该工厂生成的任务的异常处理。同时,您仍然可以等待此类任务的完成或以“一劳永逸”的方式使用它们:


var task1 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); } );

var task2 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); },

                                      c => {    Console.WriteLine("Exception!"); },

                                      c => {    Console.WriteLine("Success!"  ); } );


task1.Wait(); // You can omit this

task2.Wait(); // You can omit this

但是,老实说,我不太确定为什么要使用完成处理代码。无论如何,此决定取决于您的应用程序的逻辑。


查看完整回答
反对 回复 2019-10-17
  • 2 回答
  • 0 关注
  • 1099 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信