一、Task 实例化方式及启动(含代码示例)
1. new Task()
+ Start()
(冷任务,需手动启动)
-
特点:通过
new Task()
创建的是 "冷任务"(未启动),必须调用Start()
才会执行。 -
支持
Action
(无返回值)和Action<T>
(带参数)委托。
// 1.1 无参数无返回值 Task task1 = new Task(() => { Console.WriteLine("任务1执行:无参数无返回值"); }); task1.Start(); // 必须手动启动 task1.Wait(); // 等待任务完成 // 1.2 带参数(通过 Action<object> 传递,需装箱) Task task2 = new Task((obj) => { string message = obj as string; Console.WriteLine($"任务2执行:参数={message}"); }, "Hello Task"); // 第二个参数为传递的数据 task2.Start(); task2.Wait();
2. Task.Run()
(热任务,自动启动,推荐)
-
特点:.NET 4.5 推出,简化任务创建,直接在 ThreadPool 上启动 "热任务"(无需手动 Start)。
-
支持
Func<TResult>
(带返回值)和Action
(无返回值)。
// 2.1 无返回值 Task task3 = Task.Run(() => { Console.WriteLine("任务3执行:Task.Run 无返回值"); }); task3.Wait(); // 2.2 带返回值(Task<T>) Task<int> task4 = Task.Run(() => { return 100 + 200; // 返回结果 }); int result4 = task4.Result; // 获取返回值(会阻塞,推荐用 await) Console.WriteLine($"任务4结果:{result4}");
3. Task.Factory.StartNew()
(热任务,功能更灵活)
-
特点:通过工厂模式创建任务,可配置任务选项(如是否长期运行、调度方式等)。
-
适合复杂场景(如指定
TaskCreationOptions
)。
// 3.1 基础用法(自动启动) Task task5 = Task.Factory.StartNew(() => { Console.WriteLine("任务5执行:Factory.StartNew"); }); task5.Wait(); // 3.2 带返回值 + 配置选项 Task<string> task6 = Task.Factory.StartNew(() => { return "任务6的返回值"; }, TaskCreationOptions.LongRunning); // 标记为长期运行(可能创建新线程而非用ThreadPool) string result6 = await task6; // 推荐用 await 获取结果 Console.WriteLine($"任务6结果:{result6}");
二、向任务传递数据(委托的使用)
-
核心:通过委托的参数传递(
Action<object>
或泛型委托)。 -
推荐:复杂参数用自定义类封装,避免多次装箱。
// 示例:传递复杂参数 public class TaskParam { public int Id { get; set; } public string Name { get; set; } } // 使用 Action<object> 接收参数 Task task7 = Task.Run(() => { TaskParam param = new TaskParam { Id = 1, Name = "Test" }; ProcessParam(param); }); // 处理参数的方法 static void ProcessParam(TaskParam param) { Console.WriteLine($"处理参数:Id={param.Id}, Name={param.Name}"); }
三、任务管理(取消任务)
-
核心类:
CancellationTokenSource
(CTS)和CancellationToken
(令牌)。 -
流程:创建 CTS → 传递令牌给任务 → 任务中检查取消请求 → 调用
Cancel()
触发取消。
// 创建取消令牌源 CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; // 启动可取消的任务 Task task8 = Task.Run(() => { for (int i = 0; i < 10; i++) { // 检查是否取消请求(IsCancellationRequested) if (token.IsCancellationRequested) { Console.WriteLine("任务8已取消"); token.ThrowIfCancellationRequested(); // 可选:抛出异常标记取消 break; } Console.WriteLine($"任务8执行中:{i}"); Thread.Sleep(100); } }, token); // 传递令牌 // 3秒后取消任务 Task.Run(async () => { await Task.Delay(3000); cts.Cancel(); // 触发取消 }); try { await task8; } catch (OperationCanceledException) { Console.WriteLine("任务8被取消(捕获异常)"); } finally { cts.Dispose(); // 释放资源 }
四、任务等待(同步 / 异步等待)
方法 | 作用 | 特点(是否阻塞) |
---|---|---|
task.Wait() | 等待单个任务完成 | 阻塞当前线程 |
Task.WaitAll(tasks) | 等待所有任务完成 | 阻塞当前线程 |
Task.WaitAny(tasks) | 等待任一任务完成 | 阻塞当前线程 |
Task.WhenAll(tasks) | 异步等待所有任务完成(返回 Task) | 不阻塞,需配合 await |
Task.WhenAny(tasks) | 异步等待任一任务完成(返回 Task<Task>) | 不阻塞,需配合 await |
// 示例:异步等待(推荐,不阻塞) Task taskA = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("A完成"); }); Task taskB = Task.Run(() => { Thread.Sleep(2000); Console.WriteLine("B完成"); }); // 等待所有任务完成(异步,不阻塞) await Task.WhenAll(taskA, taskB); Console.WriteLine("A和B都完成了"); // 等待任一任务完成 Task firstCompleted = await Task.WhenAny(taskA, taskB); Console.WriteLine($"先完成的任务:{firstCompleted.Id}");
五、任务返回值(Task<T>
)
-
泛型任务
Task<T>
用于获取返回值,通过Result
属性(阻塞)或await
(非阻塞)获取。
// 带返回值的任务 Task<int> sumTask = Task.Run(() => { int sum = 0; for (int i = 1; i <= 100; i++) sum += i; return sum; }); // 正确方式:用 await 获取(非阻塞) int sum = await sumTask; Console.WriteLine($"1-100的和:{sum}"); // 不推荐:用 Result 获取(会阻塞当前线程) // int sum = sumTask.Result;
六、任务中的异常处理
-
任务中的异常会被封装在
Task
中,只有在等待时(Wait()
/await
)才会抛出。 -
需用
try-catch
捕获,多任务异常需通过AggregateException
处理。
// 单个任务异常 Task errorTask = Task.Run(() => { throw new InvalidOperationException("任务内部出错了!"); }); try { await errorTask; } catch (InvalidOperationException ex) { Console.WriteLine($"捕获异常:{ex.Message}"); } // 多个任务异常(AggregateException) Task taskX = Task.Run(() => { throw new Exception("X错误"); }); Task taskY = Task.Run(() => { throw new Exception("Y错误"); }); try { await Task.WhenAll(taskX, taskY); } catch (AggregateException ex) { // 遍历所有内部异常 foreach (var innerEx in ex.InnerExceptions) { Console.WriteLine($"任务异常:{innerEx.Message}"); } }
七、async/await 核心总结
-
定义异步方法:用
async
修饰方法,返回值通常为Task
(无返回值)或Task<T>
(有返回值)。async Task<string> GetDataAsync() { // 异步操作(如IO、网络请求) await Task.Delay(1000); // 模拟耗时操作 return "异步数据"; }
-
await 的作用:
-
等待
Task
/Task<T>
完成,但不阻塞当前线程(线程会返回执行其他工作)。 -
只能在
async
方法中使用。
-
-
核心优势:
-
以 "同步代码的写法" 实现异步逻辑,可读性强。
-
避免回调嵌套(回调地狱),保证代码执行顺序。
-
不阻塞 UI 线程(如 WPF/WinForm),提升用户体验。
-
总结
-
Task 是.NET 异步编程的核心,推荐用
Task.Run()
或Task.Factory.StartNew()
创建热任务。 -
任务取消依赖
CancellationTokenSource
,需在任务中主动检查取消请求。 -
等待任务优先用
await Task.WhenAll()
/WhenAny()
(非阻塞),避免Wait()
(阻塞)。 -
async/await
简化异步代码,让异步逻辑像同步代码一样直观,是现代 C# 异步编程的首选方式。