C# 并行和多线程编程——认识和使用Task

作者:雲霏霏 时间:2022-03-28 05:48:04 

目录
  •  任务和线程的区别:

  •  一、认识Task和Task的基本使用

    • 1、认识Task

    • 2、创建Task

  •  二、Task的任务控制

    • 1、Task.Wait

    • 2、Task.WaitAll

    • 3、Task.WaitAny

    • 4、Task.ContinueWith

    • 5、Task的取消

对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?

 任务和线程的区别:

1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。

2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

 一、认识Task和Task的基本使用

1、认识Task

首先来看一下Task的继承结构。Task标识一个异步操作。

C# 并行和多线程编程——认识和使用Task

可以看到Task和Thread一样,位于System.Threading命名空间下,这也就是说他们直接有密不可分的联系。下面我们来仔细看一下吧!

2、创建Task

创建Task的方法有两种,一种是直接创建——new一个出来,一种是通过工厂创建。下面来看一下这两种创建方法:


   //第一种创建方式,直接实例化
    var task1 = new Task(() =>
    {
     //TODO you code
    });

这是最简单的创建方法,可以看到其构造函数是一个Action,其构造函数有如下几种,比较常用的是前两种。

C# 并行和多线程编程——认识和使用Task


   //第二种创建方式,工厂创建
    var task2 = Task.Factory.StartNew(() =>
    {
     //TODO you code
    });

这种方式通过静态工厂,创建以个Task并运行。下面我们来建一个控制台项目,演示一下,代码如下:

要添加System.Threading.Tasks命名控件引用。


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)
  {
    var task1 = new Task(() =>
    {
     Console.WriteLine("Hello,task");
    });
    task1.Start();

var task2 = Task.Factory.StartNew(() =>
    {
     Console.WriteLine("Hello,task started by task factory");
    });

Console.Read();
  }
 }
}

这里我分别用两种方式创建两个task,并让他们运行。可以看到通过构造函数创建的task,必须手动Start,而通过工厂创建的Task直接就启动了。

下面我们来看一下Task的声明周期,编写如下代码:


var task1 = new Task(() =>
    {
     Console.WriteLine("Begin");
     System.Threading.Thread.Sleep(2000);
     Console.WriteLine("Finish");
    });
    Console.WriteLine("Before start:" + task1.Status);
    task1.Start();
    Console.WriteLine("After start:" + task1.Status);
    task1.Wait();
    Console.WriteLine("After Finish:" + task1.Status);

Console.Read();

task1.Status就是输出task的当前状态,其输出结果如下:

C# 并行和多线程编程——认识和使用Task

可以看到调用Start前的状态是Created,然后等待分配线程去执行,到最后执行完成。

从我们可以得出Task的简略生命周期:

Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。

WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

RanToCompletion:任务执行完毕。

 二、Task的任务控制

Task最吸引人的地方就是他的任务控制了,你可以很好的控制task的执行顺序,让多个task有序的工作。下面来详细说一下:

1、Task.Wait

在上个例子中,我们已经使用过了,task1.Wait();就是等待任务执行完成,我们可以看到最后task1的状态变为Completed。

2、Task.WaitAll

看字面意思就知道,就是等待所有的任务都执行完成,下面我们来写一段代码演示一下:


static void Main(string[] args)
  {
    var task1 = new Task(() =>
    {
     Console.WriteLine("Task 1 Begin");
     System.Threading.Thread.Sleep(2000);
     Console.WriteLine("Task 1 Finish");
    });
    var task2 = new Task(() =>
    {
     Console.WriteLine("Task 2 Begin");
     System.Threading.Thread.Sleep(3000);
     Console.WriteLine("Task 2 Finish");
    });

task1.Start();
    task2.Start();
    Task.WaitAll(task1, task2);
    Console.WriteLine("All task finished!");

Console.Read();
  }

其输出结果如下:

C# 并行和多线程编程——认识和使用Task

可以看到,任务一和任务二都完成以后,才输出All task finished!

3、Task.WaitAny

这个用发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行,将上面的代码WaitAll替换为WaitAny,输出结果如下:

C# 并行和多线程编程——认识和使用Task

4、Task.ContinueWith

就是在第一个Task完成后自动启动下一个Task,实现Task的延续,下面我们来看下他的用法,编写如下代码:


static void Main(string[] args)
  {
    var task1 = new Task(() =>
    {
     Console.WriteLine("Task 1 Begin");
     System.Threading.Thread.Sleep(2000);
     Console.WriteLine("Task 1 Finish");
    });
    var task2 = new Task(() =>
    {
     Console.WriteLine("Task 2 Begin");
     System.Threading.Thread.Sleep(3000);
     Console.WriteLine("Task 2 Finish");
    });

task1.Start();
    task2.Start();
    var result = task1.ContinueWith<string>(task =>
    {
     Console.WriteLine("task1 finished!");
     return "This is task result!";
    });

Console.WriteLine(result.Result.ToString());

Console.Read();
  }

执行结果如下:

C# 并行和多线程编程——认识和使用Task

可以看到,task1完成之后,开始执行后面的内容,并且这里我们取得task的返回值。

在每次调用ContinueWith方法时,每次会把上次Task的引用传入进来,以便检测上次Task的状态,比如我们可以使用上次Task的Result属性来获取返回值。我们还可以这么写:


var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })
             .ContinueWith<bool>(s => { return true; })
             .ContinueWith<string>(r =>
              {
                if (r.Result)
                {
                 return "Finished";
                }
                else
                {
                 return "Error";
                }
              });
    Console.WriteLine(SendFeedBackTask.Result);

首先输出Get some data,然后执行第二个获得返回值true,最后根据判断返回Finished或error。输出结果:

Get some Data!

Finished

其实上面的写法简化一下,可以这样写:


Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});

输出One,这个可以看明白了吧~

 更多ContinueWith用法参见:http://technet.microsoft.com/zh-CN/library/dd321405

5、Task的取消

前面说了那么多Task的用法,下面来说下Task的取消,比如我们启动了一个task,出现异常或者用户点击取消等等,我们可以取消这个任务。

如何取消一个Task呢,我们通过cancellation的tokens来取消一个Task。在很多Task的Body里面包含循环,我们可以在轮询的时候判断IsCancellationRequested属性是否为True,如果是True的话就return或者抛出异常,抛出异常后面再说,因为还没有说异常处理的东西。

下面在代码中看下如何实现任务的取消,代码如下:


var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;
    var task = Task.Factory.StartNew(() =>
    {
     for (var i = 0; i < 1000; i++)
     {
       System.Threading.Thread.Sleep(1000);
       if (token.IsCancellationRequested)
       {
        Console.WriteLine("Abort mission success!");
        return;
       }
     }
    }, token);
    token.Register(() =>
    {
     Console.WriteLine("Canceled");
    });
    Console.WriteLine("Press enter to cancel task...");
    Console.ReadKey();
    tokenSource.Cancel();
    Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示

这里开启了一个Task,并给token注册了一个方法,输出一条信息,然后执行ReadKey开始等待用户输入,用户点击回车后,执行tokenSource.Cancel方法,取消任务。其输出结果如下:

C# 并行和多线程编程——认识和使用Task

好了,今天先说道这里,明天继续讲task,接下来该说说task的异常处理和其他的一些用法,如果喜欢可以关注我,一有更新会马上通知你。

 作者:雲霏霏

 博客地址:http://www.cnblogs.com/yunfeifei/

来源:https://www.cnblogs.com/yunfeifei/p/4106318.html

标签:c#,并行,多线程编程,task
0
投稿

猜你喜欢

  • 详解Java设计模式之备忘录模式的使用

    2023-09-10 09:38:32
  • Logback日志基础及自定义配置代码实例

    2022-09-04 01:01:41
  • Jar包冲突问题原理及解决方案

    2023-03-05 09:40:02
  • 浅谈C#中的委托、事件与异步

    2022-04-25 23:05:34
  • Android Handler移除Message详解及实例代码

    2022-04-07 06:51:00
  • Java使用easyExcel导出excel数据案例

    2022-02-21 19:39:27
  • java中压缩文件并下载的实例详解

    2022-01-01 04:59:12
  • 关于EntityWrapper的in用法

    2023-11-29 09:02:11
  • IDEA2022中部署Tomcat Web项目的流程分析

    2023-02-26 17:19:09
  • 基于springboot搭建的web系统架构的方法步骤

    2023-11-21 13:33:51
  • Java String 拼接字符串原理详解

    2023-05-14 10:10:33
  • Java中lombok的@Builder注解的解析与简单使用详解

    2022-12-29 06:44:55
  • C#实现把科学计数法(E)转化为正常数字值

    2022-09-06 23:41:47
  • Java编程实现判断网上邻居文件是否存在的方法

    2022-12-27 09:49:46
  • springboot项目启动,但是访问报404错误的问题

    2022-09-21 10:30:10
  • Java 图表类库详解

    2021-11-09 00:25:11
  • Android N多窗口支持

    2022-08-08 19:27:37
  • JavaWeb中Servlet的深入讲解

    2022-03-07 21:12:36
  • mybatis-plus查询源码详解

    2023-02-02 11:58:02
  • mybatis-plus的批量新增/批量更新以及问题

    2022-10-28 04:44:16
  • asp之家 软件编程 m.aspxhome.com