异步
异步
异步编程的核心概念
async 关键字
- 用于标记一个方法为异步方法。
- 异步方法的返回类型通常是
Task、Task<T>或ValueTask。
Task:表示一个没有返回值的异步操作。
Task<T>:表示一个返回类型为T的异步操作。
ValueTask:轻量版的Task,适用于高性能场景。
await 关键字
- 用于暂停异步方法的执行,直到等待的任务完成。
await只能用于async方法中。- 它不会阻塞线程,而是将控制权交回给调用方,直到任务完成后再恢复执行。
异步编程的基本语法
定义异步方法
public async Task GetDataAsync()
{
// 异步操作
await Task.Delay(1000); // 模拟耗时操作
}
调用异步方法
public async Task CallAsyncMethod()
{
await GetDataAsync(); // 等待异步方法完成
Console.WriteLine("异步方法已调用.");
}
并行
var call1 = Call1();
var call2 = Call2();
await Task.WhenAll(call1, call2);
async Task MethodAsync(int seconds) => await Task.Delay(seconds);
async Task Call1()
{
"start call1".Dump();
await MethodAsync(2000);
"done call1".Dump();
}
async Task Call2()
{
"start call2".Dump();
await MethodAsync(2000);
"done call2".Dump();
}
概念
- 异步不意味着多线程
- 异步默认借助线程池
- 异步要求不阻塞,多线程经常阻塞(Thread.Sleep())
- 多线程适合CPU密集型操作,创建与销毁线程开销大,提供底层控制,不易于传参和返回,代码繁琐
- 异步适合I/O密集型操作,适合短暂的小任务,提高系统响应能力
异步任务(Task)
包含了异步任务的各种状态的引用类型:(另有ValueTask值类型版本)
var task = new Task<string>( () =>
{
Thread.Sleep(1500);
return "done";
});
task.Status.Dump();
task.Start();
task.Status.Dump();
Thread.Sleep(1000);
task.Status.Dump();
Thread.Sleep(501);
task.Status.Dump();
task.Result.Dump("Result");
对异步任务的抽象:开启异步任务后,当前线程不会阻塞,而是可以做其他事情,异步任务会借助线程池在其他线程上运行,获取结果后回到之前的状态。
任务的结果:
- 返回值为Task的方法表示异步任务没有返回值
- 返回值为Task
表示有类型为T的返回值
异步方法(async Task)
- 方法标记为async后,可以在方法中使用await关键字
- await关键字会等待异步任务结束,获得结果
- async+await会把方法包装成状态机,await类似于检查点
- async void缺少记录状态的Task对象,无法聚合异常,需要谨慎处理异常,几乎只用于对于事件的注册(Action)
- 传染性:移除async,处处async;几乎所有自带方法都提供了异步的版本(如果没有,可以用Task.Run包装)
重要思想:不阻塞!
- await会释放当前线程,使其可以执行其他工作,而不必阻塞线程直到异步操作完成
- 不要在异步方法里面用任何方式阻塞当前线程(Task.Wait() Task.Result() 阻塞线程,容易导致死锁)
- Thread.Sleep -> Task.Delay 前者阻塞,后者异步会释放当前线程
同步上下文
一种管理和协调线程的机制,允许开发者将代码的执行 切换到特定的线程
WinForms和WPF默认拥有同步上下文(UI线程),控制台默认没有
Task.ConfigureAwait( false ):配置任务通过await方法结束后,若为true则回到原线程 一般只有UI线程会使用
TaskScheduler
一发即忘
调用一个异步方法,但不使用await或阻塞去等待结束,无法观察任务的状态
int HeavyJob()
{
Console.WriteLine($"HeavyJob 开始: {Environment.CurrentManagedThreadId}");
Thread.Sleep(3000);
Console.WriteLine($"HeavyJob 结束");
return 43;
}
Console.WriteLine($"主线程开始: {Environment.CurrentManagedThreadId}");
var task = Task.Run(() => HeavyJob());
// 在等待期间做其他事情
for(int i = 1; i <= 8; i++)
{
Console.WriteLine($"等待期间做其他事");
Thread.Sleep(500);
}
int result = await task; // 此时可能已经完成,或者继续等待
Console.WriteLine($"结果: {result}");
Task.Factory.StartNew() 相比Task.Run提供更多功能

浙公网安备 33010602011771号