基础知识
1.什么是异步任务
- 包含了异步任务的各种状态的一个引用类型
1)正在运行、完成、结果、报错等
2)另有ValueTask值类型版本 - 对于异步任务的抽象
1)开启异步任务后,当前线程并不会阻塞,而是可以去做其他事情
2)异步任务(默认)会借助线程池在其他线程上运行
3)获取结果后回到之前的状态 - 任务结果
1)返回值为Task的方法表示异步任务没有返回值
2)返回值为Task则表示有类型为T的返回值
2.异步方法(async Task)
- 将方法标记async后,可以在方法中使用await关键字
- await关键字会等待异步任务,并获得结果
- async+await会将方法包装成状态机,await类似于检查点(MoveNext方法会被底层调用,从而切换状态)
- async Task
- async void
- 异步编程具有传染性
3.不阻塞
- await会展示释放当前线程,使得该线程可以执行其他工作,而不必阻塞线程直到异步操作完成
- 不要在异步方法里面用任何方式阻塞当前线程
- 常见阻塞情况
1)Task.Wait() & Task.Result()如果任务没有完成,则会阻塞当前线程,容易导致死锁
2)Task.Delay() 是一个异步任务,会立刻释放当前线程
3)Thread.Sleep() 会阻塞当前的线程,这与异步编程的理念不符
4)IO等操作的同步方法
5)其他繁重且耗时的任务
4.同步上下文
- 一种管理和协调线程的机制,允许开发者将代码的执行切换到特定线程
- WinForms 与 WPF 拥有同步上下文(UI线程),而控制台程序默认没有
- ConfigureAwait(false)
1)配置任务通过await方法结束后是否会到原来的线程,默认为true
2)一般只有UI线程会采用这种策略 - TaskScheduler
1)控制Task的调度方式和运行线程
1.1)线程池线程Default
1.2)当前线程CurrentThread
1.3)单线程上下文 STAThread
1.4)长时间运行线程LongRunning
2)优先级、上下文、执行状态等
5.一发即忘
- 调用一个异步方法,但是并不使用await或阻塞的方式去等待它的结果
- 无法观察任务状态(是否完成、是否报错等)
二.常见误区
1异步是否一定是多线程?
- 异步编程不必需要多线程来实现(时间片轮转调度)
- 比如可以在单个线程上使用异步I/O或事件驱动的编程模型(EAP)
- 单线程异步:自己定好计时器,到时间之前先去做别的事
- 多线程异步:将任务交给不同的线程,并由自己来进行指挥调度
2.异步方法一定要写成 async Task?
- async关键字知识用来配合await使用,从而将方法包装成状态机
- 本质上任然是Task,只不过提供了语法糖,并且函数体中可以直接return Task的泛型类型
- 接口中无法声明async Task
3.await一定会切换同步上下文?
- 在使用await关键字调用并等待一个异步任务时,异步方法不一定会立刻来到新的线程上
- 如果 await 了一个已经完成的任务(包括Task.Delay(0)),会直接获得结果
4.异步编程可以取代多线程
- 异步编程和多线程有一定关系,但是两者并不是可以完全相互代替的
5.Task.Result一定会阻塞当前线程?
- 如果任务已经完成,那么Task.Result可以直接得到结果
6.开启的异步任务一定不会阻塞当前线程?
await关键字不一定会立刻释放当前线程,所以如果调用的异步方法中存在阻塞(如 Thread.Sleep(0)),那么依旧会阻塞当前线程上下文对应的线程