现在的位置: 首页 > 综合 > 正文

.NET(C#):多个await和和异常处理

2012年02月28日 ⁄ 综合 ⁄ 共 2129字 ⁄ 字号 评论关闭

第一点需要提的就是第一个await发生异常后,后续的await不会进行,但是注意此时的Task仍然会进行的,当然后面的Task如果抛出异常的话,异常则属于未觉察异常,这个未觉察异常在.NET 4.0会在垃圾回收时引发程序崩溃,但在.NET 4.5 beta后不会引发进程崩溃。更多相关信息可以参(.NET 4.5中关于Task的未觉察异常的更新),这里就不多讲了。

 

当第一个异常被抛出后,我们可以直接在这个await上进行try和catch来捕获异常,就像该方法被同步执行一样,而事实上,这个方法会被await而分割成两半,前面的会同步执行,而await后面的代码则会被附加在对应Task结束后执行(如果Task在此时没有结束的话),同时当前环境的SynchronizationContext会被用来调用await后的代码。

 

当然编译器做了相当大的功劳,让我们可以简单的按照普通异常处理的方式来处理异步调用的异常处理。

 

来看下面这个叫doo的async方法,方法运行开始会运行两个Task,分别在1秒后和4秒后抛出异常,然后在后面await这两个Task,代码:

static async Task doo()

{

    try

    {

        Console.WriteLine("ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

        var t1 = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("Task1即将抛出异常"); throw new Exception("Task1异常"); });

        var t2 = Task.Run(() => { Thread.Sleep(4000); Console.WriteLine("Task2即将抛出异常"); throw new Exception("Task2异常"); });

        await t1;

        Console.WriteLine("Task1的await执行完毕");

        await t2;

        Console.WriteLine("Task2的await执行完毕");

    }

    catch (Exception ex)

    {

        Console.WriteLine("ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine("捕获异常:" + ex.Message);

    }

}

 

 

 

接着在主方法中调用另一个async方法来await整个doo方法返回的Task,代码如下:

static void Main(string[] args)

{

    test();

    Thread.Sleep(Timeout.Infinite);

}

 

static async void test()

{

    await doo();

    Console.WriteLine("test方法结束");

}

 

 

等待几秒后,整个程序的完整输出是这样:

ManagedThreadId: 1

Task1即将抛出异常

ManagedThreadId: 3

捕获异常:Task1异常

test方法结束

Task2即将抛出异常

 

可以看到,当Task1抛出异常后,异常被捕获,Task2不会被await,当然Task2仍然在运行。

 

从代码上看,try-catch在同一个方法内,给人造成是同步执行的假象,事实上,通过输出当前线程的ManagedThreadId我们可以看到:执行try和catch代码区域的完全是两个线程。

 

最后如果在此类Task上再调用Wait会有什么结果?答案则是同普通Wait一样,需要捕获AggregateException异常,然后通过它的InnerExceptions属性访问包含的子异常,不过同await的结果也一样,此时异常也只会是第一个await中Task的异常,后续Task异常值会被默默吞掉(如果这些Task外部没有引用的话),同时Wait方法不会等待其他没有await的Task的执行。

 

示例代码:

首先把doo中的try-catch去掉。

然后主方法:

static void Main(string[] args)

{

    try

    {

        doo().Wait();

    }

    catch (AggregateException ae)

    {

        foreach(var exp in ae.InnerExceptions)

            Console.WriteLine(exp.Message);

        Console.WriteLine("结束catch");

    }

 

    Thread.Sleep(Timeout.Infinite);

}

 

结果:

Task1即将抛出异常

Task1异常

结束catch

Task2即将抛出异常

 

当catch结束后,另一个Task的异常才被抛出,Wait没有等它,同时AggregateException也不包含另一个Task的异常。

抱歉!评论已关闭.