C# - Task的各种用法和详解
1、Task简介【*所有的线程任务都会随着主线程的退出而退出】
- ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
- ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
- ThreadPool不支持线程执行的先后次序;
- 以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,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 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Task1 { public void TaskMethod1() { Task t = new Task(() => { Console.WriteLine( "任务开始..." ); Thread.Sleep(5000); }); t.Start(); t.ContinueWith((task) => { Console.WriteLine( "任务完成,完成时的状态为:" ); Console.WriteLine( "IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}" , task.IsCanceled, task.IsCompleted, task.IsFaulted); }); } } } |
2、Task用法【new只是创建了一个任务,需要Start才会执行,Task.Run是直接开始执行】
2.1创建任务
- 不带返回参数的
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 29 30 31 32 33 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Task2 { public void TaskMethod1( string param) { Console.WriteLine($ "输入参数:{param}" ); } public void TaskMethod2() { //方式1 var t1 = new Task(() => TaskMethod1( "无返回值方式1.1" )); var t2 = new Task(() => TaskMethod1( "无返回值方式1.2" )); t1.Start(); t2.Start(); Task.WaitAll(t1, t2); //会等待所有任务结束,主线程才会退出 //方式2 Task.Run(() => TaskMethod1( "无返回值方式2" )); //方式3 Task.Factory.StartNew(() => TaskMethod1( "无返回值方式3" )); //异步方法 //or Task t3 = Task.Factory.StartNew(() => TaskMethod1( "无返回值方式3" )); t3.Wait(); } } } |
- async/await的实现方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Task2 { /// <summary> /// async/await的实现方式: /// </summary> public async void TaskMethod3() { //Task.Delay方法只会延缓异步方法中后续部分执行时间,当程序执行到await表达时,一方面会立即返回调用方法,执行调用方法中的剩余部分,这一部分程序的执行不会延长。另一方面根据Delay()方法中的参数,延时对异步方法中后续部分的执行。 await Task.Delay(1000); Console.WriteLine( "执行异步方法" ); for ( int i = 0; i < 10; i++) { Console.WriteLine(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 | using System; namespace ThreadStudy { class Program { static void Main( string [] args) { //Task1 task1 = new Task1(); //task1.TaskMethod1(); Task2 task2 = new Task2(); Console.WriteLine( "主线程执行其他任务..." ); task2.TaskMethod3(); Console.WriteLine( "主线程执行其他处理..." ); for ( int i = 0; i < 10; i++) { Console.WriteLine( "主线程{i}" ); } Console.ReadKey(); } } } |
- 带返回值得方式
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 29 30 31 32 33 34 35 36 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { public class Task3 { int TestMethod1() { Console.WriteLine( "测试方法" ); Thread.Sleep(2000); return 12; } public Task< int > TaskMethod1() { return Task.Run(() => TestMethod1()); //Task<int>.Run(() => TestMethod1()); 简化了<int> } public int TestMethod2() { int sum = 0; Console.WriteLine( "执行异步操作" ); for ( int i = 0; i < 100; i++) { sum += i; } Thread.Sleep(1000); return sum; } } } |
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 29 30 31 32 33 34 35 36 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { //方式1 Task3 task3 = new Task3(); Task< int > task = task3.TaskMethod1(); int result = task.Result; //方法执行完,主线程才会结束 Console.WriteLine($ "1 = {result}" ); //方式2 task = task3.TaskMethod1(); Console.WriteLine(task.Status); while (!task.IsCompleted) { Console.WriteLine(task.Status); Thread.Sleep(200); } Console.WriteLine(task.Status); result = task.Result; Console.WriteLine($ "2 = {result}" ); //方式3 Task< int > task2 = Task.Run(() => task3.TestMethod2()); Console.WriteLine( "主线程执行其他操作" ); //task2.Wait();Result为必须执行完,主线程才结束,所以这里不写Wait不影响 Console.WriteLine($ "3 = {task2.Result}" ); } } } |
- async/await方式
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 29 30 31 32 33 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { var task = AsyncMethod1(); Console.WriteLine( "主线程执行其他处理" ); for ( int i = 0; i < 3; i++) { Console.WriteLine($ "Main{i}" ); } int result = task.Result; Console.WriteLine(result); } async static Task< int > AsyncMethod1() { await Task.Delay(1000); //立刻返回执行调用方法的后续部分,延迟1秒之后后面的部分 int sum = 0; Console.WriteLine( "使用Task执行异步操作." ); for ( int i = 0; i < 1000; i++) { sum += i; } return sum; } } } |
2.2组合任务ContinueWith
- 简单demo
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 29 30 31 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { Task< int > task = new Task< int >(() => { int sum = 0; Console.WriteLine( "任务1" ); for ( int i = 0; i < 100; i++) { sum += i; } return sum; }); task.Start(); Console.WriteLine( "主线程执行其他任务" ); Task task1 = task.ContinueWith(t => { Thread.Sleep(1000); Console.WriteLine($ "task = {t.Result}" ); }); task1.Wait(); //task.Wait();对t.Result不起作用,主线程会直接结束 } } } |
- 任务的串行【没有研究】
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { ConcurrentStack< int > stack = new ConcurrentStack< int >(); //t1先串行 var t1 = Task.Factory.StartNew(() => { stack.Push(1); stack.Push(2); }); //t2,t3并行执行 var t2 = t1.ContinueWith(t => { int result; stack.TryPop( out result); Console.WriteLine( "Task t2 result={0},Thread id {1}" , result, Thread.CurrentThread.ManagedThreadId); }); //t2,t3并行执行 var t3 = t1.ContinueWith(t => { int result; stack.TryPop( out result); Console.WriteLine( "Task t3 result={0},Thread id {1}" , result, Thread.CurrentThread.ManagedThreadId); }); //等待t2和t3执行完 Task.WaitAll(t2, t3); //t7串行执行 var t4 = Task.Factory.StartNew(() => { Console.WriteLine( "当前集合元素个数:{0},Thread id {1}" , stack.Count, Thread.CurrentThread.ManagedThreadId); }); t4.Wait(); } } } |
- 子任务
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 29 30 31 32 | using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { Task< string []> task = new Task< string []>(state => { Console.WriteLine( "父任务开始" ); string [] result = new string [2]; new Task(() => { result[0] = "子任务1" ; }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { result[1] = "子任务2" ; }, TaskCreationOptions.AttachedToParent).Start(); Thread.Sleep(1000); return result; }, "我是父任务,创建了子任务,等子任务执行完才会执行结束" ); task.ContinueWith(t => { Array.ForEach(t.Result, r => Console.WriteLine(r)); }); task.Start(); task.Wait(); } } } |
- 动态并行(TaskCreationOptions.AttachedToParent) 父任务等待所有子任务完成后 整个任务才算完成【没有研究过】
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Node { public Node Left { get ; set ; } public Node Right { get ; set ; } public string Text { get ; set ; } } class Program { static Node GetNode() { Node root = new Node { Left = new Node { Left = new Node { Text = "L-L" }, Right = new Node { Text = "L-R" }, Text = "L" }, Right = new Node { Left = new Node { Text = "R-L" }, Right = new Node { Text = "R-R" }, Text = "R" }, Text = "Root" }; return root; } static void Main( string [] args) { Node root = GetNode(); DisplayTree(root); } static void DisplayTree(Node root) { var task = Task.Factory.StartNew(() => DisplayNode(root), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); task.Wait(); } static void DisplayNode(Node current) { if (current.Left != null ) Task.Factory.StartNew(() => DisplayNode(current.Left), CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); if (current.Right != null ) Task.Factory.StartNew(() => DisplayNode(current.Right), CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); Console.WriteLine( "当前节点的值为{0};处理的ThreadId={1}" , current.Text, Thread.CurrentThread.ManagedThreadId); } } } |
2.3取消任务 CancellationTokenSource【没研究明白】
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { private static int TaskMethod( string name, int seconds, CancellationToken token) { Console.WriteLine( "Task {0} is running on a thread id {1}. Is thread pool thread: {2}" , name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); for ( int i = 0; i < seconds; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); if (token.IsCancellationRequested) return -1; } return 42 * seconds; } private static void Main( string [] args) { var cts = new CancellationTokenSource(); var longTask = new Task< int >(() => TaskMethod( "Task 1" , 10, cts.Token), cts.Token); Console.WriteLine(longTask.Status); cts.Cancel(); Console.WriteLine(longTask.Status); Console.WriteLine( "First task has been cancelled before execution" ); cts = new CancellationTokenSource(); longTask = new Task< int >(() => TaskMethod( "Task 2" , 10, cts.Token), cts.Token); longTask.Start(); for ( int i = 0; i < 5; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(longTask.Status); } cts.Cancel(); for ( int i = 0; i < 5; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(longTask.Status); } Console.WriteLine( "A task has been completed with result {0}." , longTask.Result); } } } |
2.4处理任务中的异常
- 单个任务
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 29 30 31 32 33 34 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static int TaskMethod( string name, int seconds) { Console.WriteLine( "Task {0} is running on a thread id {1}. Is thread pool thread: {2}" , name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds)); throw new Exception( "Boom!" ); return 42 * seconds; } static void Main( string [] args) { try { Task< int > task = Task.Run(() => TaskMethod( "Task 2" , 2)); int result = task.GetAwaiter().GetResult(); Console.WriteLine( "Result: {0}" , result); } catch (Exception ex) { Console.WriteLine( "Task 2 Exception caught: {0}" , ex.Message); } Console.WriteLine( "----------------------------------------------" ); Console.WriteLine(); } } } |
- 多个任务
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static int TaskMethod( string name, int seconds) { Console.WriteLine( "Task {0} is running on a thread id {1}. Is thread pool thread: {2}" , name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds)); throw new Exception( string .Format( "Task {0} Boom!" , name)); return 42 * seconds; } public static void Main( string [] args) { try { var t1 = new Task< int >(() => TaskMethod( "Task 3" , 3)); var t2 = new Task< int >(() => TaskMethod( "Task 4" , 2)); var complexTask = Task.WhenAll(t1, t2); var exceptionHandler = complexTask.ContinueWith(t => Console.WriteLine( "Result: {0}" , t.Result), TaskContinuationOptions.OnlyOnFaulted ); t1.Start(); t2.Start(); Task.WaitAll(t1, t2); } catch (AggregateException ex) { ex.Handle(exception => { Console.WriteLine(exception.Message); return true ; }); } } } } |
- async/await方式
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | using System; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static async Task ThrowNotImplementedExceptionAsync() { throw new NotImplementedException(); } static async Task ThrowInvalidOperationExceptionAsync() { throw new InvalidOperationException(); } static async Task Normal() { await Fun(); } static Task Fun() { return Task.Run(() => { for ( int i = 1; i <= 10; i++) { Console.WriteLine( "i={0}" , i); Thread.Sleep(200); } }); } static async Task ObserveOneExceptionAsync() { var task1 = ThrowNotImplementedExceptionAsync(); var task2 = ThrowInvalidOperationExceptionAsync(); var task3 = Normal(); try { //异步的方式 Task allTasks = Task.WhenAll(task1, task2, task3); await allTasks; //同步的方式 //Task.WaitAll(task1, task2, task3); } catch (NotImplementedException ex) { Console.WriteLine( "task1 任务报错!" ); } catch (InvalidOperationException ex) { Console.WriteLine( "task2 任务报错!" ); } catch (Exception ex) { Console.WriteLine( "任务报错!" ); } } public static void Main() { Task task = ObserveOneExceptionAsync(); Console.WriteLine( "主线程继续运行........" ); task.Wait(); } } } |
2.5Task.FromResult的应用
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static IDictionary< string , string > cache = new Dictionary< string , string >() { { "0001" , "A" }, { "0002" , "B" }, { "0003" , "C" }, { "0004" , "D" }, { "0005" , "E" }, { "0006" , "F" }, }; public static void Main() { Task< string > task = GetValueFromCache( "0006" ); Console.WriteLine( "主程序继续执行。。。。" ); string result = task.Result; Console.WriteLine( "result={0}" , result); } private static Task< string > GetValueFromCache( string key) { Console.WriteLine( "GetValueFromCache开始执行。。。。" ); string result = string .Empty; //Task.Delay(5000); Thread.Sleep(5000); Console.WriteLine( "GetValueFromCache继续执行。。。。" ); if (cache.TryGetValue(key, out result)) { return Task.FromResult(result); } return Task.FromResult( "" ); } } } |
2.6使用IProgress实现异步编程的进程通知【没有研究】
- IProgress<in T>只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress<in T>的实现类Progress<in T>的构造函数接收类型为Action<T>的形参,通过这个委托让进度显示在UI界面中。
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void DoProcessing(IProgress< int > progress) { for ( int i = 0; i <= 100; ++i) { Thread.Sleep(100); if (progress != null ) { progress.Report(i); } } } static async Task Display() { //当前线程 var progress = new Progress< int >(percent => { Console.Clear(); Console.Write( "{0}%" , percent); }); //线程池线程 await Task.Run(() => DoProcessing(progress)); Console.WriteLine( "" ); Console.WriteLine( "结束" ); } public static void Main() { Task task = Display(); task.Wait(); } } } |
2.7 C#Task返回值
Task返回值,目前有2种情况,一种是异步async返回值,一种是同步返回值
- 第一种:异步返回值
- Task方法如果加了async关键字,那么就是异步返回方法,如果是异步返回方法,需要返回一个值时,直接return value,就可以了。
- 第二种:同步返回值
- Task方法如果没有加async关键字,需要返回一个值时,使用Task.FromResult方法,Task.FromResult(value)就可以了。
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 | using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ThreadStudy { class Program { static void Main( string [] args) { Console.WriteLine(TaskMethod().Result); Console.WriteLine(TaskMethod2().Result); } async static Task< int > TaskMethod() { return 1; } static Task< int > TaskMethod2() { return Task.FromResult(2); } } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现