需求:比如说开始一个项目,首先拆分模块交给不同的人干,全部干完之后进行整合测试,项目完结。其中拆分模块之后每个人都做不同的工作,这是完全可以并行操作的,所以可以使用多线程,比如:
代码:
/// <summary> /// Task专题解析 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"****** **btnTask_Click 异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************"); Console.WriteLine("项目开始"); Console.WriteLine("拆分模块"); Console.WriteLine("指定开发人员开发对应模块"); Task.Run(() => this.Coding("张三", "Website")); Task.Run(() => this.Coding("李四", "WebApi")); Console.WriteLine("整合测试"); Console.WriteLine("项目结束"); Console.WriteLine($"**** *****btnTask_Click 异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************"); } /// <summary> /// 编码模拟 /// </summary> /// <param name="name"></param> /// <param name="project"></param> private void Coding(string name, string project) { Console.WriteLine($"***** *********Coding start {name}- {project}-{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}** ***********"); long IResult = 0; for (int i = 0; i < 1000000000; i++) { IResult = i; } Console.WriteLine($"***** ***********Coding end {name}- {project}-{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}** **********"); }
结果:
****** **btnTask_Click 异步方法 start 1-2021-09-17 18:21:07************************* 项目开始 拆分模块 指定开发人员开发对应模块 整合测试 项目结束 **** *****btnTask_Click 异步方法 end 1 -2021-09-17 18:21:07 ************************* **************************Coding start 张三- Website-4-2021-09-17 18:21:07************************* **************************Coding start 李四- WebApi-7-2021-09-17 18:21:07************************* **************************Coding end 李四- WebApi-7-2021-09-17 18:21:09************************* **************************Coding end 张三- Website-4-2021-09-17 18:21:09*************************
从这个结果可以看出,不符合实际情况,因为之后模块全部开发完成之后才能项目结束。所以改成下面代码:
/// <summary> /// Task专题解析 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"****** **btnTask_Click 异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************"); Console.WriteLine("项目开始"); Console.WriteLine("拆分模块"); Console.WriteLine("指定开发人员开发对应模块"); List<Task> taskList = new List<Task>(); taskList.Add(Task.Run(() => this.Coding("张三", "Website"))); taskList.Add(Task.Run(() => this.Coding("李四", "WebApi"))); // 等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。 Task.WaitAll(taskList.ToArray()); Console.WriteLine("整合测试"); Console.WriteLine("项目结束"); Console.WriteLine($"**** *****btnTask_Click 异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************"); }
结果:
****** **btnTask_Click 异步方法 start 1-2021-09-17 18:26:19************************* 项目开始 拆分模块 指定开发人员开发对应模块 *** **Coding start 张三- Website-3-2021-09-17 18:26:19***** *** *** **Coding start 李四- WebApi-4-2021-09-17 18:26:19***** *** *** ****Coding end 李四- WebApi-4-2021-09-17 18:26:22********** * *** ****Coding end 张三- Website-3-2021-09-17 18:26:22********** * 整合测试 项目结束 **** *****btnTask_Click 异步方法 end 1 -2021-09-17 18:26:22 *************************
还有一种方法WaitAny,只要传入参数中的任何一个线程结束了,就可以再继续往下执行。
private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"****** **btnTask_Click 异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************"); Console.WriteLine("项目开始"); Console.WriteLine("拆分模块"); Console.WriteLine("指定开发人员开发对应模块"); List<Task> taskList = new List<Task>(); taskList.Add(Task.Run(() => this.Coding("张三", "Website"))); taskList.Add(Task.Run(() => this.Coding("李四", "WebApi"))); //只有taskList中有一个线程执行完毕就不会再阻塞当前主线程,使其继续往下执行 Task.WaitAny(taskList.ToArray()); Console.WriteLine("项目开发里程碑达成"); // 等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。 Task.WaitAll(taskList.ToArray()); Console.WriteLine("整合测试"); Console.WriteLine("项目结束"); Console.WriteLine($"**** *****btnTask_Click 异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************"); }
结果:李四率先完成,所以可以接着向下执行。
****** **btnTask_Click 异步方法 start 1-2021-09-17 18:35:30************************* 项目开始 拆分模块 指定开发人员开发对应模块 *** **Coding start 张三- Website-3-2021-09-17 18:35:30***** *** *** **Coding start 李四- WebApi-4-2021-09-17 18:35:30***** *** *** ****Coding end 李四- WebApi-4-2021-09-17 18:35:32********** * 项目开发里程碑达成 *** ****Coding end 张三- Website-3-2021-09-17 18:35:32********** * 整合测试 项目结束 **** *****btnTask_Click 异步方法 end 1 -2021-09-17 18:35:32 *************************
但是上面这2种方法都是阻塞的主线程,所以当前的界面是不能再响应其他操作(只有主线程才能响应界面操作),比如无法拖动,点击等。
可以使用TaskFactory类来解决:
/// <summary> /// Task专题解析 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"****** **btnTask_Click 异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************"); Console.WriteLine("项目开始"); Console.WriteLine("拆分模块"); Console.WriteLine("指定开发人员开发对应模块"); List<Task> taskList = new List<Task>(); taskList.Add(Task.Run(() => this.Coding("张三", "Website"))); taskList.Add(Task.Run(() => this.Coding("李四", "WebApi"))); TaskFactory taskFactory = new TaskFactory(); //任一线程执行完毕之后取消主线程的阻塞 taskFactory.ContinueWhenAny(taskList.ToArray(), t => { Console.WriteLine($"{t.AsyncState}第一个完成"); }); //全部线程执行完毕之后取消主线程的阻塞 taskFactory.ContinueWhenAll(taskList.ToArray(), tArr => { Console.WriteLine($"整合测试"); });
Task.WaitAll(taskList.ToArray());
Console.WriteLine($"项目完成");
Console.WriteLine($"**** *****btnTask_Click 异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************"); }
结果:
****** **btnTask_Click 异步方法 start 1-2021-09-17 19:04:28************************* 项目开始 拆分模块 指定开发人员开发对应模块 项目完成 **** *****btnTask_Click 异步方法 end 1 -2021-09-17 19:04:28 ************************* *** **Coding start 李四- WebApi-7-2021-09-17 19:04:29***** *** *** **Coding start 张三- Website-6-2021-09-17 19:04:29***** *** *** ****Coding end 李四- WebApi-7-2021-09-17 19:04:31********** * 第一个完成 *** ****Coding end 张三- Website-6-2021-09-17 19:04:31********** * 整合测试
但是由于多线程是无序性的,看上面的结果,项目完成这句话的输出跑前面去了,比项目测试还早,按照业务逻辑来说只能先测试没问题了才能宣告项目的结束。
private void btnTask_Click(object sender, EventArgs e) { Console.WriteLine($"****** **btnTask_Click 异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************"); Console.WriteLine("项目开始"); Console.WriteLine("拆分模块"); Console.WriteLine("指定开发人员开发对应模块"); List<Task> taskList = new List<Task>(); taskList.Add(Task.Run(() => this.Coding("张三", "Website"))); taskList.Add(Task.Run(() => this.Coding("李四", "WebApi"))); TaskFactory taskFactory = new TaskFactory(); //任一线程执行完毕之后取消主线程的阻塞 taskFactory.ContinueWhenAny(taskList.ToArray(), t => { Console.WriteLine($"{t.AsyncState}第一个完成"); }); //改成下面这种形式,因为ContinueWhenAll方法的返回值是一个Task,这样的话在下面Task.WaitAll(taskList.ToArray());中就可以阻塞所有的子线程了,就能够按照顺序执行 taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArr => { Console.WriteLine($"整合测试"); })); Task.WaitAll(taskList.ToArray()); Console.WriteLine($"项目完成"); Console.WriteLine($"**** *****btnTask_Click 异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************"); }
结果:
****** **btnTask_Click 异步方法 start 1-2021-09-17 19:43:26************************* 项目开始 拆分模块 指定开发人员开发对应模块 *** **Coding start 张三- Website-3-2021-09-17 19:43:26***** *** *** **Coding start 李四- WebApi-4-2021-09-17 19:43:26***** *** *** ****Coding end 张三- Website-3-2021-09-17 19:43:28********** * 第一个完成 *** ****Coding end 李四- WebApi-4-2021-09-17 19:43:28********** * 整合测试 项目完成 **** *****btnTask_Click 异步方法 end 1 -2021-09-17 19:43:28 *************************