C#多线程编程的Task(任务全面解析)
原文链接:https://www.cnblogs.com/xietianjiao/p/7429742.html
Task是.NET4.0加入的,跟线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。
我们可以说Task是一种基于任务的编程模型。它与thread的主要区别是,它更加方便对线程进程调度和获取线程的执行结果。
Task类和Task<TResult>类
前者接收的是Action委托类型
后者接收的是Func<TResult>委托类型
任务和线程的区别:
1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比
线程池有很小的开销和精确的控制。
一、Task的创建
1、直接创建
1 2 3 4 5 6 7 8 | var task1 = new Task(() => { Console.WriteLine( "Begin" ); System.Threading.Thread.Sleep(5000); Console.WriteLine( "Finish" ); }); Console.WriteLine( "Before start:" + task1.Status); task1.Start(); |
2、工厂创建
1 2 3 | Task.Factory.StartNew(()={ }); |
3、4.5以后Run运行
1 2 3 | Task.Run(()=>{ }); |
4、一种方便获取返回值的方式
1 2 3 4 5 6 7 8 9 10 | static void Main( string [] args) { var tcs = new TaskCompletionSource< int >(); new Thread(() => { Thread.Sleep(5000); int i = Enumerable.Range(1, 100).Sum(); tcs.SetResult(i); }).Start(); //线程把运行计算结果,设为tcs的Result。 Task< int > task = tcs.Task; Console.WriteLine(task.Result); //此处会阻塞,直到匿名线程调用tcs.SetResult(i)完毕 } |
二、细节解释
看下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | namespace WpfApplication6 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); ConsoleManager.Show(); //打开控制台窗口 } private void Window_Loaded( object sender, RoutedEventArgs e) { Console.WriteLine( "主线程启动" ); Task task = Task.Run(() => { Thread.Sleep(1500); Console.WriteLine( "task启动" ); }); Thread.Sleep(300); task.Wait(); Console.WriteLine( "主线程结束" ); } } } |
结果:
分析:
开启新任务的方法:Task.Run()或者Task.Factory.StartNew(),开启的是后台线程
要在主线程中等待后台线程执行完毕,可以使用Wait方法(会以同步的方式来执行)。不用Wait则会以异步的方式来执行。
thread和Task的区别,thread new多少个就会创建多少个线程,而task是利用线程池中的线程。
task<TResult>就是有返回值的Task,TResult就是返回值类型。示例:
1 2 3 4 5 6 7 8 9 10 11 12 | private void Window_Loaded( object sender, RoutedEventArgs e) { Console.WriteLine( "主线程开始" ); //返回值类型为string Task< string > task = Task< string >.Run(() => { Thread.Sleep(2000); return Thread.CurrentThread.ManagedThreadId.ToString(); }); //会等到task执行完毕才会输出; Console.WriteLine(task.Result); Console.WriteLine( "主线程结束" ); } |
通过task.Result可以取到返回值,若取值的时候,后台线程还没执行完,则会等待其执行完毕!
简单提一下:
Task任务可以通过CancellationTokenSource类来取消。
三、Task的其他方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Task.Wait(); //阻塞当前线程 Task.WaitAll(); //阻塞当前线程直到所有的任务执行完毕 Task.WaitAny(); //阻塞当前线程直到有任意一个任务执行完毕 Task.ContinueWith( task=>{ }); //执行完上一个任务后继续执行,并将上一个任务(包括结果)传递给下一个代码块 一种是使用GetAwaiter方法。GetAwaiter方法返回一个TaskAwaiter结构,该结构有一个OnCompleted事件,只需对 OnCompleted事件赋值,即可在完成后调用该事件。 static void Main( string [] args) { Task< int > Task1 = Task.Run< int >(() => { return Enumerable.Range(1, 100).Sum(); }); var awaiter = Task1.GetAwaiter(); awaiter.OnCompleted(() => { Console.WriteLine( "Task1 finished" ); int result = awaiter.GetResult(); Console.WriteLine(result); // Writes result }); Thread.Sleep(1000); } |
四、任务的中断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { for ( var i = 0; i < 1000; i++) { System.Threading.Thread.Sleep(1000); if (token.IsCancellationRequested) { Console.WriteLine( "Abort mission success!" ); return ; } } }, token); //注册cancel后要执行的代码 token.Register(() => { Console.WriteLine( "Canceled" ); }); Console.WriteLine( "Press enter to cancel task..." ); Console.ReadKey(); //调用取消 tokenSource.Cancel(); |
五、任务的中断取消
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { for ( var i = 0; i < 1000; i++) { System.Threading.Thread.Sleep(1000); if (token.IsCancellationRequested) { Console.WriteLine( "Abort mission success!" ); return ; } } }, token); //注册cancel后要执行的代码 token.Register(() => { Console.WriteLine( "Canceled" ); }); Console.WriteLine( "Press enter to cancel task..." ); Console.ReadKey(); //调用取消 tokenSource.Cancel(); |
六、异常处理
1 2 3 4 5 6 7 8 9 10 11 12 | 异常处理; 对于某些匿名的Task(通过 Task.Run方法生成的,不调用wait,也不关心是否运行完成),某些情况下,记录它们的异 常错误也是有必要的。这些异常称作未观察到的异常(unobserved exceptions)。可以通过订阅一个全局的静态事件 TaskScheduler.UnobservedTaskException来处理这些异常。只要当一个Task有异常,并且在被垃圾回收的时候,才会触 发这一个事件。如果Task还处于被引用状态,或者只要GC不回收这个Task,这个UnobservedTaskException事件就不会被 触发 GC.Collect(); GC.WaitForPendingFinalizers(); |
七、Task的状态
Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。
WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。
RanToCompletion:任务执行完毕。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战