C#-Task详细学习记录(包含一个WPF_Demo)
目录
一、Task 核心概念
Task 本质:对“未来结果”的一个抽象(Promise) + 异步调度机制
它是 C# async/await 的基础。
二、Task 常用 API 分类
1.创建任务
Task.Run
在线程池上启动一个任务。
用于在线程池执行任务(CPU 密集)
await Task.Run(() => DoWork());Task.FromResult
返回一个已完成的成功任务
return Task.FromResult(42);适用于:已有结果但需要返回 Task
Task.FromException
返回一个已失败的任务
return Task.FromException<int>(new Exception("error"));Task.FromCanceled
返回一个已取消的任务
return Task.FromCanceled<int>(token);TaskCompletionSource(重要)
var tcs = new TaskCompletionSource<int>();
tcs.SetResult(100);
await tcs.Task;用途:回调转 async
2.任务组合
Task.WhenAll
等待所有任务完成(全部成功才算成功)
await Task.WhenAll(task1, task2);特点:全部完成才返回
Task.WhenAny
等待任意一个任务完成(谁先完成就返回谁)
var t = await Task.WhenAny(task1, task2);特点:谁先完成返回谁
3. 时间控制
Task.Delay
异步等待一段时间(不会阻塞线程)
await Task.Delay(1000);Task.Yield
强制让出当前线程,稍后继续指向(用于避免同步上下文阻塞)
await Task.Yield();4.状态与结果
推荐:
var result = await task;不推荐:
var r = task.Result; // 可能死锁
task.Wait();5. 实例方法(较少用)
ContinueWith
当前任务完成后继续执行另一个任务
task.ContinueWith(t => Console.WriteLine("done"));已被 await 替代
三、关键机制解析
1. 为什么 await 不会死锁?
因为:
- await 不阻塞线程
- 状态机 + 回调机制
而 .Result / Wait:
- 阻塞线程
- 导致 UI 线程互相等待
2. async void 为什么危险?
- 无法捕获异常
- 无法 await
- 无法控制生命周期
仅用于:事件处理
3.ConfigureAwait(false)
await task.ConfigureAwait(false);作用:不捕获 UI 上下文
好处:
- 防死锁
- 提升性能
适用于:库代码 / 后台逻辑
四、常见错误总结
-
用 Task.Run 包裹 I/O
-
使用 .Result
-
忘记 await
-
async void 滥用
五、WPF 实战 Demo(完整示例)
场景说明
点击按钮后:
- 并发执行多个任务
- 显示进度
- 支持取消
- 使用 WhenAll / WhenAny / Delay / Run / ContinueWith
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Task Demo" Height="300" Width="400">
<StackPanel Margin="20">
<Button Name="StartBtn" Click="StartBtn_Click" Content="Start" />
<Button Name="CancelBtn" Click="CancelBtn_Click" Content="Cancel" />
<TextBlock Name="Output" Margin="0,20,0,0" />
</StackPanel>
</Window>using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window
{
private CancellationTokenSource _cts;
public MainWindow()
{
InitializeComponent();
}
private async void StartBtn_Click(object sender, RoutedEventArgs e)
{
_cts = new CancellationTokenSource();
Output.Text = "Running...";
try
{
var tasks = Enumerable.Range(1, 3).Select(i => DoWorkAsync(i, _cts.Token));
// WhenAny 示例
var first = await Task.WhenAny(tasks);
Output.Text = $"First finished: {first.Result}";
// WhenAll 示例
var results = await Task.WhenAll(tasks);
Output.Text += "\nAll done: " + string.Join(",", results);
}
catch (Exception ex)
{
Output.Text = ex.Message;
}
}
private void CancelBtn_Click(object sender, RoutedEventArgs e)
{
_cts?.Cancel();
}
private async Task<int> DoWorkAsync(int id, CancellationToken token)
{
// 模拟异步 I/O
await Task.Delay(1000 * id, token);
// CPU 操作
await Task.Run(() =>
{
Thread.Sleep(500);
});
// ContinueWith 示例
await Task.Delay(100).ContinueWith(t => { });
token.ThrowIfCancellationRequested();
return id;
}
}
}六、Demo 涵盖知识点
-
Task.Run
-
Task.Delay
-
Task.WhenAll
-
Task.WhenAny
-
ContinueWith
-
CancellationToken
-
async/await
七、最终总结
Task = 异步世界的核心抽象
掌握关键:
-
await(核心)
-
WhenAll / WhenAny(并发)
-
Task.Run(CPU)
-
Delay(时间控制)
-
避免 .Result
Task
├── 创建任务
│ ├── Task.Run 👉 开线程池任务(最常用)
│ ├── Task.FromResult 👉 已完成任务(有返回值)
│ ├── Task.FromException 👉 已失败任务
│ ├── Task.FromCanceled 👉 已取消任务
│ └── TaskCompletionSource 👉 手动控制任务
│
├── 任务组合
│ ├── Task.WhenAll 👉 等全部完成(并发汇总)
│ └── Task.WhenAny 👉 等任意完成(抢最快)
│
├── 时间控制
│ ├── Task.Delay 👉 异步延迟(不阻塞)
│ └── Task.Yield 👉 主动让出线程
│
├── 状态/结果
│ ├── Task.CompletedTask 👉 已完成(无返回)
│ ├── task.Result 👉 获取结果(⚠️阻塞)
│ └── await task 👉 推荐方式
│
├── 同步等待(⚠️危险)
│ ├── task.Wait()
│ ├── Task.WaitAll()
│ └── Task.WaitAny()
│
└── 延续/链式
└── ContinueWith 👉 旧式写法(基本被 await 替代)八、进阶建议
下一步建议深入:
- async/await 状态机原理
- SynchronizationContext
- 线程池调度机制