Task是.NET 3.0中推出的,是基于ThreadPool封装的,里面的线程都是来自于ThreadPool。

1、使用Run()方法启动线程

F12查看Run()方法的定义:

发现Run()方法的参数是一个Action类型的委托,那么可以使用下面的方式启动多线程:

 // 使用Run()方法启动线程
 Task.Run(() => this.DoSomethingLong("btnTask_Click1"));
 Task.Run(() => this.DoSomethingLong("btnTask_Click2"));

2、使用TaskFactory的StartNew()方法

 // 使用TaskFactory启动
 TaskFactory taskFactory = Task.Factory;
 taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click3"));

3、使用Task的构造函数启动

 // 使用构造函数启动
 Task task= new Task(() => this.DoSomethingLong("btnTask_Click4"));
 task.Start();

效果:

下面来看看一个案例:

拿编写程序为例来说明:在一个项目中,项目经理负责前期的一些准备工作,相当于是主线程,其他开发人员负责具体的编码工作,相当于子线程,那么可以使用这个例子来模仿多线程。

1、先编写一个编码的方法:

 /// <summary>
 /// 编码的方法
 /// </summary>
 /// <param name="name">开发人员Name</param>
 /// <param name="project">负责的模块</param>
 private void Coding(string name, string project)
 {
        Console.WriteLine($")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        ;
        ; i < ; i++)
        {
             lResult += i;
        }
        //Thread.Sleep(2000);

        Console.WriteLine($")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
 }

2、启动线程

Console.WriteLine($")}】");
Console.WriteLine($")}】");
Console.WriteLine($")}】");
Task.Run(() => this.Coding("Tom", "Client"));
Task.Run(() => this.Coding("Jack", "Service"));

结果:

这时又提出了新的需求:必须所有编码工作都完成以后才能上线使用,将代码修改如下:

 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Task.Run(() => this.Coding("Tom", "Client"));
 Task.Run(() => this.Coding("Jack", "Service"));
  Console.WriteLine($")}】");

再次运行程序,效果如下:

从上面的截图中看出:这不是我们想要的效果,编码工作还没有结束就可以上线使用了,即子线程还没有结束,主线程就已经结束了。要想实现我们想要的效果,那么必须使主线程等待子线程都结束以后,主线程才能结束。这时可以使用Task提供的WaitAll()方法实现,F12查询WaitAll()方法的定义:

WaitAll()方法有很多重载,我们在这里使用第一个重载方法,即参数是Task[]数组。查看Run()方法的定义时,我们会发现Run()方法的返回值就是Task类型,那么我们可以将代码进行如下的修改:

 List<Task> taskList = new List<Task>();
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 taskList.Add( Task.Run(() => this.Coding("Tom", "Client")));
 taskList.Add( Task.Run(() => this.Coding("Jack", "Service")));
 // 等待集合中的所有线程都执行完
 Task.WaitAll(taskList.ToArray());
 Console.WriteLine($")}】");

然后运行程序,查看效果:

可以看出:这次就是我们想要的效果。但是在运行程序的时候会发现,使用了WaitAll()方法以后,界面会卡住,也就是说WaitAll()方法会阻塞当前线程,等着全部任务都执行完以后,才会进入下一行。

注意:WaitAll()除了上面使用的方法以外,还有带时间参数的重载方法,表示会等待多长时间,无论所有任务是否都完成。例如:

 List<Task> taskList = new List<Task>();
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 taskList.Add( Task.Run(() => this.Coding("Tom", "Client")));
 taskList.Add( Task.Run(() => this.Coding("Jack", "Service")));
 //限时等待,最多等待1秒
 Task.WaitAll(taskList.ToArray(), );
 Console.WriteLine("等待1s之后,执行的动作");
 // 等待集合中的所有线程都执行完
 Task.WaitAll(taskList.ToArray());
 Console.WriteLine($")}】");

效果:

这时需求又有了变化:只要其中一个模块完成,就相当于完成了里程碑的工作,这时可以用Task提供的WaitAny()方法实现。F12查询WaitAny()的定义:

WaitAny()表示等待其中任何一个任务完成就会进入下一行,代码修改如下:

 List<Task> taskList = new List<Task>();
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 taskList.Add( Task.Run(() => this.Coding("Tom", "Client")));
 taskList.Add( Task.Run(() => this.Coding("Jack", "Service")));
 Task.WaitAny(taskList.ToArray());
 Console.WriteLine($")}】");
 ////限时等待,最多等待1秒
 //Task.WaitAll(taskList.ToArray(), 1000);
 //Console.WriteLine("等待1s之后,执行的动作");
 //// 等待集合中的所有线程都执行完
 Task.WaitAll(taskList.ToArray());
 Console.WriteLine($")}】");

效果:

注意:WaitAny()同样也会阻塞当前线程,卡住界面。和WaitAll()一样,WaitAny()也有带时间参数的重载方法,表示等待多长时间。

应用场景:

1、WaitAll():假如一个界面需要的数据,来自不同的数据源,那么这时可以使用WaitAll()等待所有的数据都查询完成以后才显示界面。

2、WaitAny():商品查询,只要查询出某一个符合条件的即可。

上面讲到的WaitAll()和WaitAny()都会卡住界面,那么有没有不卡界面的方法呢?答案是肯定的:那就是WhenAll()和WhenAny().。

从上面的截图中可以看出,WhenAll()的参数还是一个Task[]类型的数组,返回值是Task类型,表示Task[]数组里面的所有任务都完成以后,创建一个新的Task。这时可以继续调用Task类提供的ContinueWith()方法。ContinueWith()方法表示Task任务完成时异步执行的延续任务。

 List<Task> taskList = new List<Task>();
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 Console.WriteLine($")}】");
 taskList.Add(Task.Run(() => this.Coding("Tom", "Client")));
 taskList.Add(Task.Run(() => this.Coding("Jack", "Service")));

 Task.WhenAny(taskList.ToArray()).ContinueWith(t =>
 {
          Console.WriteLine($")}】");
 });

 Task.WhenAll(taskList.ToArray()).ContinueWith(t =>
 {
          Console.WriteLine($")}】");
  });

效果:

注意:

除了Task可以实现这种效果以为,TaskFactory也可以实现,例如:

 TaskFactory taskFactory = new TaskFactory();
 taskFactory.ContinueWhenAll(taskList.ToArray(), tList =>
 {
           Console.WriteLine($")}】");
 });

 taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
 {
            Console.WriteLine($")}】");
 });

多线程三:Task的更多相关文章

  1. 【多线程】 Task

    [多线程] Task 一. 常用方法: 1. ContinueWith : 当前 Task 完成后, 执行传入的 Task 2. Delay : 创建一个等待的 Task,只有在调用 Wait 方法时 ...

  2. 多线程(三) java中线程的简单使用

    java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...

  3. java 多线程三

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 注意到 java 多线程一 中 MyThread2 运行结果出现0.-1,那是因为在操作共享数据时没有加锁导致. 加锁的 ...

  4. 【多线程】 Task ,async ,await

    [多线程]Task ,async ,await 一. WinForm 里经常会用到多线程, 多线程的好出就不多说了,来说说多线程比较麻烦的地方 1. UI 线程与其他线程的同步,主要是 Form 和 ...

  5. 细说.NET中的多线程 (三 使用Task)

    上一节我们介绍了线程池相关的概念以及用法.我们可以发现ThreadPool. QueueUserWorkItem是一种起了线程之后就不管了的做法.但是实际应用过程,我们往往会有更多的需求,比如如果更简 ...

  6. JAVA基础知识之多线程——三种实现多线程的方法及区别

    所有JAVA线程都必须是Thread或其子类的实例. 继承Thread类创建线程 步骤如下, 定义Thead子类并实现run()方法,run()是线程执行体 创建此子类实例对象,即创建了线程对象 调用 ...

  7. Java多线程——<三>简单的线程执行:Executor

    一.概述 按照<Java多线程——<一><二>>中所讲,我们要使用线程,目前都是显示的声明Thread,并调用其start()方法.多线程并行,明显我们需要声明多个 ...

  8. 【多线程】Task

    介绍 Task是.NET推出数据任务处理的工作类.位于System.Threading.Tasks命名空间下,通过命名空间也可以看出是个多线程类. 创建Task: Task有很多构造函数,无参有参都有 ...

  9. java多线程(三)——锁机制synchronized(同步语句块)

    用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...

随机推荐

  1. 转:zTree高级入门:如何通过扩展节点的属性来达到是否显示节点的删除编辑等图标(按钮)

    当我们在使用ztree树组件的节点编辑功能时,只要我们引入了ztree相关节点编辑的js脚本文件: <script type="text/javascript" src=”/ ...

  2. Jmeter 线程组、运行次数参数化

    Jmeter的jmx文件保存了线程数和运行次数等参数,这个参数可以在命令行中传入参数的方式来修改数值 步骤如下 1.生成线程和运行次数的参数 Jmeter选项中函数助手对话框,选中__P参数,这个参数 ...

  3. html select与option标签

    1.select 元素 select 元素可创建单选或多选菜单.<select> 元素中的 <option> 标签用于定义列表中的可用选项 常用属性如下: autofocus: ...

  4. php分享二十九:命名空间

    1:命名空间的命名不区分大小写 2:namespace必须在所有代码之前,除了declare语法以外(不过他之前可以有注释,空行等) 3:只有以下类型的代码受命名空间的影响,它们是:类(包括抽象类和t ...

  5. tensorflow省钱方案-ml-engine

    google cloud有专门的ml-engine(machine learning engine)模块,可以直接用来跑tensorflow,不用像虚拟机一样开关机.只需要根据需要指定配置就行.收费分 ...

  6. 【R】R语言常用函数

    R语言常用函数 基本 一.数据管理vector:向量 numeric:数值型向量 logical:逻辑型向量character:字符型向量 list:列表 data.frame:数据框c:连接为向量或 ...

  7. vue.js 首屏优化

    我们以 vue-cli 工具为例,使用 vue-router 搭建SPA应用,UI框架选用 element-ui , ajax方案选用 axios, 并引入 vuex ,使用 vuex-router- ...

  8. 从头开始学习vue-router

    一.前言 要学习vue-router就要先知道这里的路由是什么?为什么我们不能像原来一样直接用标签编写链接哪?vue-router如何使用?常见路由操作有哪些?等等这些问题,就是本篇要探讨的主要问题. ...

  9. 菜鸟学Java(十三)——将MyEclipse项目导入到Eclipse

    最近由于种种原因,需要将以前用MyEclipse写的项目迁移到Eclipse中.但是当我将之前的项目import到Eclipse中后,发现根本不能运行.经过一番寻觅,终于让我找到了一种解决的办法.一起 ...

  10. 菜鸟学数据库(五)——MySQL必备命令

    今天跟大家分享一下MySQL从连接到具体操作的一系列常用命令.可能有的人觉得现在有很多可视化的工具,没必要再学习那些具体的命令了,但是我不这么认为,不可否认那些工具的确让我们的工作更加方便快捷,但是如 ...