Task异步编程,刨根到底
1. 编译器到底对await做了什么
await 一个异步操作的时候,实际上编译器会创建一个状态机,这个状态机包含了调用者的上下文变量,状态机使用
yield
迭代器实现,状态机由clr调度,每次运行都会重新加入回队列,直到Task
完成或异常结束
2.Task有哪些实现方式
经常我们可以看到一些库中使用
TaskCompletionSource
来创建Task
,改变Task
的状态,实际上TaskCompletionSource
只是在Task基础上做简单的封装操作
TaskCompletionSource
会直接创建一个Task
,设置Result
也是直接在Task
上设置结果,不同手动操作的地方就是做了自旋等待Task.IsCompleted
再返回,保证线程安全性
所以Task的实现
就是将Task
的状态切换补完的一个过程,让其成为一个Actor模型
,实现方式可以自由发挥
3.clr到底怎么调度Task
所有的Task都是由
TaskScheduler
调度,最终进入到ThreadPool
队列中,然后clr获取队列中的工作项并运行
Task.Wait()
操作会先将Task
移出TaskScheduler
然后再使用当前线程执行
TaskScheduler
有两种Scheduler
实现,默认是ThreadPoolTaskScheduler
Task.Run
将会在内部 new Task
创建一个任务,并将Task
添加到默认的TaskScheduler.Default
中
TaskScheduler
默认是使用ThreadPoolTaskScheduler
实现
Task.Run
调用TaskScheduler.QueueTask
方法将Task添加到任务队列中,在ThreadPoolTashScheduler
中并没有保存任务队列,而是直接调用
ThreadPool
中的UnsafeQueueCustomWorkItem
方法添加到ThreadPool
中,由ThreadPool
管理任务队列
ThreadPool
中有一个全局的工作队列,所有异步任务都将会加入到队列中,并由clr去获取队列中的IThreadPoolWorkItem
运行
ThreadPool
根据工作队列的数量,首先会先创建CPU核心数
数量的工作线程,并以每秒一个
的速度创建新线程
在ThreadPool
中,规定了任务的时间片,每个时间片为30个时钟
,每个线程运行满一个时间片才会返回线程池
CPU时钟使用Environment.TickCount
来计算