zoukankan      html  css  js  c++  java
  • 5天玩转C#并行和多线程编程 —— 第四天 Task进阶

     

     一、Task的嵌套

       Task中还可以再嵌套Task,Thread中能不能这样做,我只能说我是没这样写过。Task中的嵌套,我感觉其实也可以分开来写,不过嵌套起来会方便管理一点。Task中的嵌套分为两种,关联嵌套和非关联嵌套,就是说内层的Task和外层的Task是否有联系,下面我们编写代码先来看一下非关联嵌套,及内层Task和外层Task没有任何关系,还是在控制台程序下面,代码如下:

    复制代码
    static void Main(string[] args)
          {
             var pTask = Task.Factory.StartNew(() => 
             {
                var cTask = Task.Factory.StartNew(() =>
                {
                   System.Threading.Thread.Sleep(2000);
                   Console.WriteLine("Childen task finished!");
                });
                Console.WriteLine("Parent task finished!");
             });
             pTask.Wait();
             Console.WriteLine("Flag");
             Console.Read();
          }
    复制代码

    运行后,输出以下信息:

    从图中我们可以看到,外层的pTask运行完后,并不会等待内层的cTask,直接向下走先输出了Flag。这种嵌套有时候相当于我们创建两个Task,但是嵌套在一起的话,在Task比较多时会方便查找和管理,并且还可以在一个Task中途加入多个Task,让进度并行前进。

    下面我们来看一下如何创建关联嵌套,就是创建有父子关系的Task,修改上面代码如下:

    复制代码
        static void Main(string[] args)
          {
             var pTask = Task.Factory.StartNew(() => 
             {
                var cTask = Task.Factory.StartNew(() =>
                {
                   System.Threading.Thread.Sleep(2000);
                   Console.WriteLine("Childen task finished!");
                },TaskCreationOptions.AttachedToParent);
                Console.WriteLine("Parent task finished!");
             });
             pTask.Wait();
             Console.WriteLine("Flag");
             Console.Read();
          }
    复制代码

    可以看到,我们在创建cTask时,加入了以参数,TaskCreationOptions.AttachedToParent,这个时候,cTask和pTask就会建立关联,cTask就会成为pTask的一部分,运行代码,看下结果:

    可以看到,tTask会等待cTask执行完成。省得我们写Task.WaitAll了,外层的Task会自动等待所有的子Task完成才向下走。

    下面我们来写一个Task综合使用的例子,来看一下多任务是如何协作的。假设有如下任务,如图:

    任务2和任务3要等待任务1完成后,取得任务1的结果,然后开始执行。任务4要等待任务2完成,取得其结果才能执行,最终任务3和任务4都完成了,合并结果,任务完成。图中已经说的很明白了。下面来看一下代码:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TaskDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Task.Factory.StartNew(() =>
                {
                    var t1 = Task.Factory.StartNew<int>(() => 
                    {
                        Console.WriteLine("Task 1 running...");
                        return 1;
                    });
                    t1.Wait(); //等待任务一完成
                    var t3 = Task.Factory.StartNew<int>(() =>
                    {
                        Console.WriteLine("Task 3 running...");
                        return t1.Result + 3;
                    });
                    var t4 = Task.Factory.StartNew<int>(() =>
                    {
                        Console.WriteLine("Task 2 running...");
                        return t1.Result + 2;
                    }).ContinueWith<int>(task =>
                    {
                        Console.WriteLine("Task 4 running...");
                        return task.Result + 4;
                    });
                    Task.WaitAll(t3, t4);  //等待任务三和任务四完成
                    var result = Task.Factory.StartNew(() =>
                    {
                        Console.WriteLine("Task Finished! The result is {0}",t3.Result + t4.Result);
                    });
                });
                Console.Read();
            }
        }
    }
    复制代码

    任务2和任务4可以用ContinueWith连接执行,最终运行结果如图:

    可以看到所有的任务都执行了,我们也得到了正确的结果11.这下体会到Task的强大了吧~

     二、Task的异常处理

       任何应用程序都需要有异常处理机制,谁也不能保证自己写到代码在任何时候都是可以正常运行的,那么在Task中到底该怎么处理异常呢?先来按照平时的写法,加个Try...Catch...试试,看看会出现什么现象:

    复制代码
       static void Main(string[] args)
          {
             try
             {
                var pTask = Task.Factory.StartNew(() =>
                {
                   var cTask = Task.Factory.StartNew(() =>
                   {
                      System.Threading.Thread.Sleep(2000);
                      throw new Exception("cTask Error!");
                      Console.WriteLine("Childen task finished!");
                   });
                   throw new Exception("pTask Error!");
                   Console.WriteLine("Parent task finished!");
                });
    
                pTask.Wait();
             }
             catch (Exception ex)
             {
                Console.WriteLine(ex.Message);
             }
             Console.WriteLine("Flag");
             Console.Read();
          }
    复制代码

    大家都看得懂,就不解释了,直接F5运行,结果如图:

    唉,不对啊~~怎么显示这异常信息呢?先不说异常信息对不对,反正异常是捕获到了。从这张图中你们还发现了什么吗?

    没错,cTask被中断了,这里cTask和pTask并没有建立关联,但是pTask出现异常,其内部的Task也都会中断,不再执行,即使异常是在子Task启动以后发生的。

    下面我们继续来说异常吧,来看看正确的异常处理办法,怎么捕获到真正的异常信息,代码如下:

    复制代码
         static void Main(string[] args)
          {
             try
             {
                var pTask = Task.Factory.StartNew(() =>
                {
                   var cTask = Task.Factory.StartNew(() =>
                   {
                      System.Threading.Thread.Sleep(2000);
                      throw new Exception("cTask Error!");
                      Console.WriteLine("Childen task finished!");
                   });
                   throw new Exception("pTask Error!");
                   Console.WriteLine("Parent task finished!");
                });
    
                pTask.Wait();
             }
             catch (AggregateException ex)
             {
                foreach (Exception inner in ex.InnerExceptions)
                {
                   Console.WriteLine(inner.Message);
                }
             }
             Console.WriteLine("Flag");
             Console.Read();
          }
    复制代码

    这里用了AggregateException,就是异常集合,当然开发中不会只有一个线程,肯定会有多个线程,多个线程就可能有多个异常。我们变量异常集合,输出异常信息,如下图:

    对了吧,看到正确的异常信息了,但是还是看不到cTask的,因为他被中断了。

    当然,除了在task中使用异常,我们还可以通过Task的几个属性来判断Task的状态,如:IsCompleted, IsFaulted, IsCancelled,Exception等等来判断task是否成功的执行了。

  • 相关阅读:
    codeforces 814B An express train to reveries
    codeforces 814A An abandoned sentiment from past
    codeforces 785D D. Anton and School
    codeforces 785C Anton and Fairy Tale
    codeforces 791C Bear and Different Names
    AOP详解
    Spring集成JUnit测试
    Spring整合web开发
    IOC装配Bean(注解方式)
    IOC装配Bean(XML方式)
  • 原文地址:https://www.cnblogs.com/fire909090/p/8023702.html
Copyright © 2011-2022 走看看