C# Task 篇幅一
在https://www.cnblogs.com/loverwangshan/p/10415937.html中我们有讲到委托的异步方法,Thread,ThreadPool,然后今天来讲一下Task,
ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
- ThreadPool不支持线程的取消、完成、失败通知等交互性操作
- ThreadPool不支持线程执行的先后次序
以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在.netFramwork3.0出现的Task,线程是基于线程池,然后提供了丰富的API
下面我们来初步认识一下Task,下面我们先新增一个公共的方法以下方法的演示中都会用到:
#region Private Method
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****DoSomethingLong Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}*****");
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
}
Console.WriteLine($"****DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}****");
}
#endregion
一:Task启动任务的几种方式
{
Task task = new Task(() => this.DoSomethingLong("btnTask_Click_1"));
task.Start();
}
{
Task task = Task.Run(() => this.DoSomethingLong("btnTask_Click_2"));
}
{
TaskFactory taskFactory = Task.Factory;// Task.Factory等同于: new TaskFactory()
Task task = taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click_3"));
}
二:Task.Delay()和Thread.Sleep()方法的比较运用
private void Test()
{
Console.WriteLine($"****************btnTask_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Sleep之前");
Thread.Sleep(); //同步等待--当前线程等待2s 然后继续
Console.WriteLine("在Sleep之后");
stopwatch.Stop();
Console.WriteLine($"Sleep耗时{stopwatch.ElapsedMilliseconds}");
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Console.WriteLine("在Delay之前");
Task task = Task.Delay()
.ContinueWith(t =>
{
stopwatch.Stop();
Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}");
Console.WriteLine($"This is ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
});//异步等待--等待2s后启动新任务
Console.WriteLine("在Delay之后");
}
Console.WriteLine($"****************btnTask_Click End {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
通过执行结果如下:

通过观察发现:
- Sleep是同步执行方法,即是遇到Sleep先等待,然后才能接着做其它的
- Delay是异步执行方法,一般不会单独使用,而是会跟ContinueWith等一起联合使用,即是重新开启一个线程,这个线程多少时间之后执行!
三:TaskFactory类的ContinueWhenAny 和 ContinueWhenAll
/// <summary>
/// 模拟Coding过程
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name, string projectName)
{
Console.WriteLine($"***Coding Start {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
} Console.WriteLine($"***Coding End {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***");
}
private void Test()
{
Console.WriteLine($"***btnTask_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(o => this.Coding("AA", "Portal"), "AA"));
taskList.Add(taskFactory.StartNew(o => this.Coding("BB", " DBA "), "BB"));
taskList.Add(taskFactory.StartNew(o => this.Coding("CC", "Client"), "CC"));
taskList.Add(taskFactory.StartNew(o => this.Coding("DD", "BackService"), " DD"));
taskList.Add(taskFactory.StartNew(o => this.Coding("EE", "Wechat"), "EE")); //谁第一个完成,获取一个红包奖励
taskFactory.ContinueWhenAny(taskList.ToArray(), t => Console.WriteLine($"{t.AsyncState}开发完成,获取个红包奖励{Thread.CurrentThread.ManagedThreadId.ToString("")}"));
taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), rArray => Console.WriteLine($"开发都完成,一起庆祝一下{Thread.CurrentThread.ManagedThreadId.ToString("")}")));
//ContinueWhenAny ContinueWhenAll 非阻塞式的回调;而且使用的线程可能是新线程,也可能是刚完成任务的线程,唯一不可能是主线程
Console.WriteLine($"***btnTask_Click end {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
}

通过结果我们总结如下:
- ContinueWhenAny:等待任意一条完成
- ContinueWhenAll :等待所有的完成
- ContinueWhenAny 和 ContinueWhenAll 非阻塞式的回调;而且使用的线程可能是新线程,也可能是刚完成任务的线程,唯一不可能是主线程
四:Task中的 WaitAny 和 WaitAll
/// <summary>
/// 模拟Coding过程
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name, string projectName)
{
Console.WriteLine($"***Coding Start {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
}
Thread.Sleep();
Console.WriteLine($"***Coding End {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***");
}
private void Test()
{
Console.WriteLine($"***btnTask_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(o => this.Coding("AA", "Portal"), "AA"));
taskList.Add(taskFactory.StartNew(o => this.Coding("BB", " DBA "), "BB"));
taskList.Add(taskFactory.StartNew(o => this.Coding("CC", "Client"), "CC"));
taskList.Add(taskFactory.StartNew(o => this.Coding("DD", "BackService"), " DD"));
taskList.Add(taskFactory.StartNew(o => this.Coding("EE", "Wechat"), "EE")); //阻塞当前线程,等着任意一个任务完成
Task.WaitAny(taskList.ToArray());//也可以限时等待,如果是winform界面会卡
Console.WriteLine("第一个模块已经完成,现在开始准备环境开始部署");
//需要能够等待全部线程完成任务再继续 阻塞当前线程,等着全部任务完成,如果是winform界面会卡
Task.WaitAll(taskList.ToArray());
Console.WriteLine("5个模块全部完成,准备联测");
Console.WriteLine($"***btnTask_Click end {Thread.CurrentThread.ManagedThreadId.ToString("")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
}
运行执行如下:

通过上面得到如下:
- Task.WaitAny:等待任意一条完成。例如:核心数据可能来自数据库/接口服务/分布式搜索引擎/缓存,多线程并发请求,哪个先完成就用哪个结果,其他的就不管了
- Task.WaitAll :等待所有的完成。例如:A数据库 B接口 C分布式服务 D搜索引擎,适合多线程并发,都完成后才能返回给用户,需要等待WaitAll
- Task.WaitAny 和Task.WaitAll都是阻塞当前线程,等任务完成后执行操作, 阻塞卡界面,是为了并发以及顺序控制
五:Task想要控制先后顺序,可以通过ContinueWith
这个在什么的Delay中已经看到了,可以一直使用ContinueWith来增加任务
Task.Run(() => this.DoSomethingLong("btnTask_Click")).ContinueWith(t => Console.WriteLine($"btnTask_Click已完成{Thread.CurrentThread.ManagedThreadId.ToString("")}"));//回调
六:任务想要有返回值,并且把返回值传递到ContinueWith中,可以通过如下代码实现:
Task.Run<int>(() =>
{
Thread.Sleep();
return DateTime.Now.Year;
}).ContinueWith(tInt =>
{
int i = tInt.Result; //有堵塞
});
注意:使用.Result这个会堵塞线程
七:Task的线程是源于线程池,线程池是单例的,全局唯一
我们可以通过下面代码来测试一下:
ThreadPool.SetMaxThreads(, );
for (int i = ; i < ; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
Thread.Sleep();
});
}
设置线程池最大线程为8个后,我们通过监测结果发现:同时并发的Task只有8个,而且线程Id是重复出现的,即线程是复用的。所以线程池是是全局的,以后要慎重设置线程池数量。
八:Parallel是来源于命名空间为:System.Threading.Tasks,并发执行多个Action 多线程。主线程会参与计算---阻塞界面,等于TaskWaitAll+主线程计算
具体的一些用法如下:
{
//启动5个任务
Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_1"),
() => this.DoSomethingLong("btnParallel_Click_2"),
() => this.DoSomethingLong("btnParallel_Click_3"),
() => this.DoSomethingLong("btnParallel_Click_4"),
() => this.DoSomethingLong("btnParallel_Click_5"));
}
{
//启动5个任务,i是从0-4
Parallel.For(, , i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
{
//启动5个任务
Parallel.ForEach(new int[] { , , , , }, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
上面三种方法都是可以的。然后Parallel是线程阻塞,那我们可不可以不堵塞线程呢,这个我们可以重启一个任务,让子线程去做这个事情,如下:
Task.Run(() =>
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = ;
Parallel.For(, , options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
});
这是一种思路,以后会有很多地方用到。
九:控制线程数量
上面的Task和TaskFactory以及Parallel都已经学习完了,我们晓的线程是根据电脑资源有关系的,有时候批量启动N个线程,效率还不如单线程高,因为会有线程切换是要耗时的,那我们怎么控制保证一次调用多个线程呢,下面提供Task和Parallel的两种解决方案,具体如下:
{
//Parallel设置最大线程为3
ParallelOptions options = new ParallelOptions();
//设置最大线程为3,以后Parallel想要控制线程数量只需要设置ParallelOptions类中的MaxDegreeOfParallelism即可
options.MaxDegreeOfParallelism = ;
Parallel.For(, , options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
{
//Task设置最大线程为3
List<Task> taskList = new List<Task>();
for (int i = ; i < ; i++)
{
int k = i;
if (taskList.Count(t => t.Status != TaskStatus.RanToCompletion) >= )
{
Task.WaitAny(taskList.ToArray());
taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
}
taskList.Add(Task.Run(() =>
{
Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("")}");
Thread.Sleep();
}));
}
}
学完上面的异步方法,Thread和Task,有人会提出以下问题:
1:什么时候能用多线程? 任务能并发的时候能够使用多线程
2:多线程能干嘛?多线程能够提升速度/优化用户体验,以cpu资源来换时间
C# Task 篇幅一的更多相关文章
- c# Task 篇幅二
上面一篇https://i.cnblogs.com/EditPosts.aspx?postid=10444773我们介绍了Task的启动,Task的一些方法以及应用,今天我们着重介绍一下Task其它概 ...
- 【转】关于Activity和Task的设计思路和方法
Activity和Task是Android Application Framework架构中最基础的应用,开发者必须清楚它们的用法和一些开发技巧.本文用大量的篇幅并通过引用实例的方式一步步深入全面讲解 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- Dynamics CRM2016 业务流程之Task Flow(一)
Task Flow 属于CRM移动端的特性,如果在项目实施中用不到CRM自带的APP或者对自APP不感冒的,那就没有往下看的必要了,移步吧. 该功能默认是不开启的,需要我们去系统设置中开启它,打勾,选 ...
- Spring源码情操陶冶#task:executor解析器
承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ...
- Ant 之 Task
Ant提供了大量的核心task和可选task,除此之外,Ant还允许用户定义自己的task,这大大扩展了Ant的功能.本书由于篇幅关系,所以不可能详细介绍Ant所有的核心task和可选task,本书将 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) 背景介绍: 为了平衡社区成员的贡献和索取,一起帮引入了帮帮币.当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮 ...
- Spark源码分析之七:Task运行(一)
在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在T ...
随机推荐
- 201771010126 王燕《面向对象设计 java》第十五周实验总结
第一部分 理论部分 ◼ JAR文件◼ 应用程序首选项存储◼ Java Web Start JAR文件: 1.Java程序的打包:程序编译完成后,程序员将.class文件压缩打包为.jar文件后,GU ...
- ES6的Module 的用法
在vue-cli中遇到的模糊参考 https://www.cnblogs.com/ppJuan/p/7151000.html 解决问题: 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 Co ...
- C++ struct结构体定义构造函数和析构函数,构造函数参数从VS2017平台转换到Qt5平台下构建出错,采用字符集转换函数将string类型转换为wstring,构建仍然出错!
调试win硬件驱动,需要利用VS编译的win驱动构建自己的Qt5GUI程序: 其中部分win驱动源码如下 device_file::device_file(const std::string& ...
- js与jq基础记录
1.在js传递参数中含加号(+)的处理方式: 只需要把js中传过去的+号替换成base64 编码 %2B:encodeURI(str).replace(/\+/g,'%2B'). 2.随机产生8位随机 ...
- HTML标签 按功能排序
按功能类别排列 New : HTML5 中的新标签. 基础 标签 描述 <!DOCTYPE> 定义文档类型. <html> 定义 HTML 文档. <title> ...
- 深入理解JVM(七)——性能监控工具
前言 工欲善其事必先利其器,性能优化和故障排查在我们大都数人眼里是件比较棘手的事情,一是需要具备一定的原理知识作为基础,二是需要掌握排查问题和解决问题的流程.方法.本文就将介绍利用性能监控工具,帮助开 ...
- 浅谈Java的主要学习要点_上海尚学堂java培训课程思维导图
Java是一种可以撰写跨平台应用程序的面向对象的程序设计语言.Java 技术具有卓越的通用性.高效性.平台移植性和安全性,广泛应用于PC.数据中心.游戏控制台.科学超级计算机.移动电话和互联网,同时拥 ...
- 【从零开始搭建自己的.NET Core Api框架】(六)泛型仓储的作用
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- [Swift]LeetCode862. 和至少为 K 的最短子数组 | Shortest Subarray with Sum at Least K
Return the length of the shortest, non-empty, contiguous subarray of A with sum at least K. If there ...
- 使用jQuery获取元素的宽度或高度的几种情况
今天说说使用jQuery获取元素大小的遇到几种情况 使用jQuery获取元素的宽度或高度的有几种情况: 1.使用width(),它只能获取当前元素的内容的宽度: 2.使用innerWidth(),它只 ...