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. 关于java代码提交HTTP POST请求中文乱码的解决方法

    首先说明下这些只是根据我工作常用经验的总结,可能不一定完全对,也不一定全面,但却是最通用的. JAVA里HTTP提交方式 httpurlconnection:jdk里自带的 httpclient:ap ...

  2. JS中parseint和number的区别

    两者定义的区别 parseInt将字符串(String)类型转为整数类型.Number() 函数把对象(Object)的值转换为数字. parseInt得到的结果是整数或者NaN,而Number得到的 ...

  3. 为jqgrid添加统计金额页脚

    为jqgrid增加自定义用户数据 设计统计的金额总和 最后在jgrid组件中添加 最后的效果为:

  4. 【Unity】4.6 灯光

    分类:Unity.C#.VS2015 创建日期:2016-04-11 一.简介 灯光(Light,也叫光源)是每一个场景的重要组成部分,用于照亮场景和对象,从而让游戏具有自己的个性和风格,比如利用灯光 ...

  5. what-is-a-closure

    https://stackoverflow.com/questions/36636/what-is-a-closure https://www.quora.com/What-are-upvalues- ...

  6. artificial%20intelligence%20a%20modern%20approach

    http://stpk.cs.rtu.lv/sites/all/files/stpk/materiali/mi/artificial%20intelligence%20a%20modern%20app ...

  7. JAVA线程池任务数大小设置

    线程池究竟设成多大是要看你给线程池处理什么样的任务,任务类型不同,线程池大小的设置方式也是不同的. 任务一般可分为:CPU密集型.IO密集型.混合型,对于不同类型的任务需要分配不同大小的线程池. CP ...

  8. [Windows Azure] Load Balancing Virtual Machines

    Load Balancing Virtual Machines All virtual machines that you create in Windows Azure can automatica ...

  9. ES6,新增数据结构Set的用法

    ES6 提供了新的数据结构 Set. 特性 似于数组,但它的一大特性就是所有元素都是唯一的,没有重复. 我们可以利用这一唯一特性进行数组的去重工作. 单一数组的去重. let set6 = new S ...

  10. godep的save和update

    godep save ./... 把依赖的包从GOPATH里找到,然后固化到当前目录Godeps目录里. 但是它不分版本:如果GOPATH里某个包升级了,godep save不会把它更新到Godeps ...