C# 多线程的最佳实践 Task
前言
在上一篇文档《C# 实现线程的常用几种方式》中记录了在C#使用多线程的常用几种实现方式,相对来说,Task才是多线程的最佳实践,那到底其他方式到底优缺点,而Task的优势有哪些?下面简单总结一下:
Thread 类方式:
优点:提供操作线程的API的多;能根据自己需要创建对应的线程;
缺点:频繁的创建和消耗比较好资源;提供操作线程的API不是马上响应(线程是操作系统统一管理,收到指令之后,具体还得操作系统真实处理,而操作系统收到指令之后并非马上执行相关指令);
TreadPool 池化方式:
优点:池化线程进行管理,需要使用就从池中获取就行,避免频繁创建和销毁线程;从而可以达到线程的复用;
缺点:提供的API太少,线程等待顺序控制比较弱;从而在一些业务情况下操作不方便;
Task:
优点:在ThreadPool的思想进行了封装,继承了ThreadPool的优点;提供了丰富的线程控制API,从而方便了开发;
Task使用--创建任务的几种方式
方式一:
#region Task创建方式1 //创建任务 线程离不开委托,因为需要处理业务,不然开启线程干嘛 Task task = new Task(() => { Console.WriteLine($"Task 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); //开启任务 task.Start(); #endregion
方式二:
#region Task创建方式2 Task task1 = Task.Run(() => { Console.WriteLine($"Task 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); #endregion
方式三:
#region Task创建方式3 Task task2 = Task.Factory.StartNew(() => { Console.WriteLine($"Task 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); #endregion
Task使用--常用实例API
常规正常流程:
static void Main(string[] args) { Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}开启"); var task = Task.Run(() => { Console.WriteLine($"Task 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}完成"); Console.ReadKey(); }
运行结果:
实例方法.Wait()
.Wait() 等待执行调用任务完成,然后执行下一步; 及阻塞了主线程;
实例方法.ContinueWith()
ContinueWith() 等调用者结束之后才进行调用里面的相关业务,由线程池分配线程进行处理接下来的业务,不阻塞主线程,但却能控制业务之间的先后顺序;
Task使用--Task中的静态API
Task.WaitAll
Task.WaitAll等到所有任务都完成之后,才进行主线程的下一步操作,即阻塞主线程;
Task.WaitAny
Task.WaitAny等到其中一个任务都完成之后,才进行主线程的下一步操作,其中任务没有完成之前也阻塞主线程;
Task使用--Task.Factory中常用API
Task.Factory.ContinueWhenAll
当ContinueWhenAll中所有任务都完成时执行回调方法,不阻塞主线程:
static void Main(string[] args) { Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}开启"); var task1 = Task.Run(() => { Console.WriteLine($"Task1 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); var task2 = Task.Run(() => { Console.WriteLine($"Task2 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(3000); }); var task3 = Task.Run(() => { Console.WriteLine($"Task3 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(5000); }); List<Task> listTask = new List<Task> { task1, task2, task3 }; Task.Factory.ContinueWhenAll(listTask.ToArray(),tasks=> { Console.WriteLine($"任务执行结束"); }); Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}完成"); Console.ReadKey(); }
执行结果:
如上图,不阻塞主线程, 当封装在所有list中的任务全部执行完成之后,再进行后续的处理,其中后续处理的业务的参数是上一完成任务的列表!!!
注: 子线程开启时,线程之间的顺序是不可控制的,由操作系统根据资源情况进行分配处理;
Task.Factory.ContinueWhenAny
当参数中的任务有一个完成之后就进行回调,执行下一个任务。
static void Main(string[] args) { Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}开启"); var task1 = Task.Run(() => { Console.WriteLine($"Task1 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(2000); }); var task2 = Task.Run(() => { Console.WriteLine($"Task2 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(3000); }); var task3 = Task.Run(() => { Console.WriteLine($"Task3 开启线程{Thread.CurrentThread.ManagedThreadId}处理业务"); Thread.Sleep(5000); }); List<Task> listTask = new List<Task> { task1, task2, task3 }; Task.Factory.ContinueWhenAny(listTask.ToArray(), tasks => { Console.WriteLine($"其中一个任务执行结束"); }); Console.WriteLine($"主线程{Thread.CurrentThread.ManagedThreadId}完成"); Console.ReadKey(); }
执行结果:
Task.Factory.ContinueWhenAny方法等其中的任务有一项完成之后就立即返回,调用后续业务,不阻塞主线程; 线程在运行过程中,之间的顺序是不可控的。
CancellationTokenSource 取消任务
在任务执行的过程中,常常会有需求进行任务的取消,有的会借用公共第三方变量进行任务控制是否继续执行,如:true执行,false不执行;在Task中,提供了CancellationTokenSource进行任务取消。如下:
如上代码说明,在task1执行完成之后,主动调用Cancel方法取消任务,task2就检测到任务取消,即退出;
自动取消:
如上图,通过指定一个时间,然后时间到了自动取消任务,也可以使用cancelTokenSource.CancelAfter(3000); 这样也能实现自动取消;
总结
以上记录一些Task常用的API,但是没有具体每个参数都进行记录,但主要功能已经说明,可以根据需求自行 查看参数进行使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架