zoukankan      html  css  js  c++  java
  • 4.0中的并行计算和多线程详解(二)

    多线程部分

    多线程在4.0中被简化了很多,仅仅只需要用到System.Threading.Tasks.::.Task类,下面就来详细介绍下Task类的使用。

    一、简单使用

    开启一个线程,执行循环方法,返回结果。开始线程为Start(),等待线程结束为Wait()。

    Code
    1.         /// <summary>
    2.         /// Task简单使用
    3.         /// </summary>
    4.         private void Demo1()
    5.         {
    6.             int i = 0;
    7.             Random r = new Random(DateTime.Now.Second);
    8.             Task t = new Task(() =>
    9.             {
    10.                 for (int v = 0; v < 100; v++)
    11.                     i += r.Next(100);
    12.             });
    13.             t.Start();
    14.             t.Wait();
    15.             Console.WriteLine("这是执行Task1后等待完成:" + i.ToString());
    16.             Console.ReadLine();
    17.         }

    比以前使用Thread方便多了吧。

    上面的例子是使用外部的变量获得结果,下面的例子是用Task<T>直接返回结果,当调用Result属性时,会自动等待线程结束,等同调用了Wait()。代码如下:

    Code
    1.         /// <summary>
    2.         /// Task带返回值
    3.         /// </summary>
    4.         private void Demo2()
    5.         {
    6.             Random r = new Random(DateTime.Now.Second);
    7.             Task<int> t = new Task<int>(() =>
    8.             {
    9.                 int i = 0;
    10.                 for (int v = 0; v < 100; v++)
    11.                     i += r.Next(100);
    12.                 return i;
    13.             });
    14.             t.Start();
    15.             Console.WriteLine("这是执行Task1获取返回值:" + t.Result.ToString());
    16.             Console.ReadLine();
    17.         }

    总结1:Task的使用比Thread简单很多,减少了同步,等待等等问题,唯一的遗憾是不支持Thread的IsBackground。

    结论1:如果不需要使用IsBackground,那么尽情的使用Task吧。

    二、线程执行完毕后调用另一个线程

    也就是两个线程,有序的执行,这里使用ContinueWith(),

    t执行完毕后再执行一个task方法,不多说了代码如下:

    Code
    1.         /// <summary>
    2.         /// Task 执行完毕后调用另一个Task
    3.         /// </summary>
    4.         private void Demo3()
    5.         {
    6.             Random r = new Random(DateTime.Now.Second);
    7.             Task<int> t = new Task<int>(() =>
    8.             {
    9.                 int i = 0;
    10.                 for (int v = 0; v < 100; v++)
    11.                     i += r.Next(100);
    12.                 return i;
    13.             });
    14.             t.ContinueWith((Task<int> task) =>
    15.             {
    16.                 Console.WriteLine("这是执行完毕Task1后继续调用Task2:" + task.Result.ToString());
    17.             });
    18.             t.Start();
    19.             Console.ReadLine();
    20.         }

    也可以直接链式的写下去,代码如下:

    Code
    1.         /// <summary>
    2.         /// Task 执行完毕后调用另一个Task(链式写法)
    3.         /// </summary>
    4.         private void Demo4()
    5.         {
    6.             Random r = new Random(DateTime.Now.Second);
    7.             Task<int> t = new Task<int>(() =>
    8.             {
    9.                 int i = 0;
    10.                 for (int v = 0; v < 100; v++)
    11.                     i += r.Next(100);
    12.                 return i;
    13.             });
    14.             Task t2 = t.ContinueWith((Task<int> task) =>
    15.             {
    16.                 Console.WriteLine(task.Result.ToString());
    17.             });
    18.             t2.ContinueWith(task =>
    19.             {
    20.                 Console.WriteLine("这是执行完毕Task1后继续调用Task2,Task2后调用Task3。");
    21.             });
    22.             t.Start();
    23.             Console.ReadLine();
    24.         }

    结论2:Task可以便捷的将几个方法串行执行。

    三、带有父子关系的线程/多线程并行开启

    t带有t1,t2,t3三个子线程,执行t的时候t1,t2,t3可并行处理,t必须等待t1,t2,t3都执行完毕后才能结束。

    创建子Task时候必须指定参数为AttachedToParent。

    Code
    1.         /// <summary>
    2.         /// 带有父子关系的Task集合,[TaskCreationOptions.AttachedToParent]
    3.         ///
    4.         /// 值                说明
    5.         /// None              默认值,此Task会被排入Local Queue中等待执行,采用LIFO模式。
    6.         /// AttachedToParent  建立的Task必须是外围的Task的子Task,也是放入Local Queue,採LIFO模式。
    7.         /// LongRunning       建立的Task不受Thread Pool所管理,直接新增一个Thread来执行此Task,无等待、无排程。
    8.         /// PreferFairness    建立的Task直接放入Global Queue中,採FIFO模式。(比上面的优先级低)
    9.         /// </summary>
    10.         private void Demo5()
    11.         {
    12.             Task<int> t = new Task<int>(() =>
    13.             {
    14.                 Task<int> t1 = new Task<int>(() =>
    15.                 {
    16.                     int i = 0;
    17.                     Random r = new Random(DateTime.Now.Second);
    18.                     for (int v = 0; v < 100; v++)
    19.                         i += r.Next(100);
    20.                     return i;
    21.                 }, TaskCreationOptions.AttachedToParent);
    22.                 Task<int> t2 = new Task<int>(() =>
    23.                 {
    24.                     int i = 0;
    25.                     Random r = new Random(DateTime.Now.Second);
    26.                     for (int v = 0; v < 100; v++)
    27.                         i += r.Next(100);
    28.                     return i;
    29.                 }, TaskCreationOptions.AttachedToParent);
    30.                 Task<int> t3 = new Task<int>(() =>
    31.                 {
    32.                     int i = 0;
    33.                     Random r = new Random(DateTime.Now.Second);
    34.                     for (int v = 0; v < 100; v++)
    35.                         i += r.Next(100);
    36.                     return i;
    37.                 }, TaskCreationOptions.AttachedToParent);
    38.                 t1.Start();
    39.                 t2.Start();
    40.                 t3.Start();
    41.                 return t1.Result + t2.Result + t3.Result;
    42.             });
    43.             t.Start();
    44.             t.Wait();
    45.             Console.WriteLine("这是带有父子关系的Task集合:" + t.Result.ToString());
    46.             Console.ReadLine();
    47.         }

    结论3:多个线程的同时开启在这里也很方便,也不用担心同步等问题。

    四、Task的中断

    这个很复杂,就不多说了,代码中有比较详细的介绍。

    Code
    1.         /// <summary>
    2.         /// 中途取消Task执行,Token
    3.         ///
    4.         /// 一是正常结束、二是产生例外、三是透过Cancel机制,这三种情况都会反映在Task.Status属性上
    5.         /// 值                              说明
    6.         /// Created                         Task已经建立,但未呼叫Start。
    7.         /// WaitingForActivation            Task已排入排程,但尚未执行(一般我们建立的Task不会有此状态,只有ContinueWith所产生的Task才会有此状态)。
    8.         /// WaitingToRun                    Task已排入排程,等待执行中。
    9.         /// Running                         Task执行中。
    10.         /// WaitingForChildrenToComplete    Task正等待子Task結束。
    11.         /// RanToCompletion                 Task已经正常执行完毕。
    12.         /// Canceled                        Task已被取消。
    13.         /// Faulted                         Task执行中发生未预期例外。
    14.         ///
    15.         /// 除了Status属性外,Task还提供了另外三个属性来判定Task状态。
    16.         /// 属性            说明
    17.         /// IsCompleted     Task已经正常执行完毕。
    18.         /// IsFaulted       Task执行中法生未预期例外。
    19.         /// IsCanceled      Task已被取消。
    20.         /// </summary>
    21.         private void Demo6()
    22.         {
    23.             CancellationTokenSource cts = new CancellationTokenSource();
    24.             var ctoken = cts.Token;
    25.             Task t1 = new Task(() =>
    26.             {
    27.                 for (int v = 0; v < 10; v++)
    28.                 {
    29.                     if (ctoken.IsCancellationRequested)
    30.                     {
    31.                         //第一种写法
    32.                         //这个会抛出异常
    33.                         ctoken.ThrowIfCancellationRequested();
    34.                         //另一种写法
    35.                         //这个不会返回异常,但是获取不到是否是中断还是执行完毕。
    36.                         //return;
    37.                     }
    38.                     Thread.Sleep(1000);
    39.                     Console.WriteLine(v);
    40.                 }
    41.             }, ctoken);
    42.             t1.Start();
    43.             Thread.Sleep(2000);
    44.             cts.Cancel();
    45.             try
    46.             {
    47.                 t1.Wait();
    48.             }
    49.             catch
    50.             {
    51.                 if (t1.IsCanceled)
    52.                     Console.WriteLine("cancel");
    53.             }
    54.             Console.ReadLine();
    55.             cts.Dispose();
    56.         }

    结论4:中断很复杂,但是对一般逻辑来说,是没有很大必要的。

    五、其他

    这里介绍下另一种写法Task.Factory,以及ContinueWhenAny和ContinueWhenAll两个方法。

    Task.Factory是静态工厂类,用于对Task提供一些麻烦的支持,这里主要介绍ContinueWhenAny和ContinueWhenAll。

    ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。

    ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。

    好了,还是看代码:

    Code
    1.         /// <summary>
    2.         /// Task.Factory
    3.         /// ContinueWhenAny和ContinueWhenAll
    4.         /// ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。
    5.         /// ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。
    6.         /// </summary>
    7.         private void Demo7()
    8.         {
    9.             Task<int> t1 = Task.Factory.StartNew<int>(() =>
    10.             {
    11.                 int total = 0;
    12.                 for (int i = 0; i < 10; i++)
    13.                     total += i;
    14.                 Thread.Sleep(12000);
    15.                 return total;
    16.             });
    17.             Task<int> t2 = Task.Factory.StartNew<int>(() =>
    18.             {
    19.                 int total = 0;
    20.                 for (int i = 10; i < 20; i++)
    21.                     total += i;
    22.                 Thread.Sleep(10000);
    23.                 return total;
    24.             });
    25.             Task tfinal = Task.Factory.ContinueWhenAny<int>(
    26.                          new Task<int>[] { t1, t2 }, (Task<int> task) =>
    27.                          {
    28.                              if (task.Status == TaskStatus.RanToCompletion)
    29.                              {
    30.                                  Console.WriteLine(task.Result);
    31.                              }
    32.                          });
    33.             Console.ReadLine();
    34.         }

    结论5:ContinueWhenAny和ContinueWhenAll对特定条件执行,还是有些用处的。

    好了,这篇文章算是完结了,4.0中的并行和线程确实简单了很多,使用起来也很方便,为了性能的提升还是要适当的使用下。

    谢谢观赏

  • 相关阅读:
    vue中的 computed 和 watch 的区别
    mysql8.0 初始化数据库及表名大小写问题
    sql server alwayson 调整数据文件路径
    zabbix 自定义监控 SQL Server
    mysql 创建用户及授权
    mysql 设置从库只读模式
    mysql8.0 主从复制安装及配置
    centos8.0安装mysql8.0
    centos8替换阿里数据源
    npm publish 报错 【you or one of your dependencies are requesting a package version that is forbidden by your security policy】
  • 原文地址:https://www.cnblogs.com/sorex/p/1830130.html
Copyright © 2011-2022 走看看