普通函数
string Func()
{
string x = X();
string y = Y();
string z = Z(); return x + y + z;
}
X(), Y(), Z()内部都Sleep 10s, 则Func()耗时30s
异步函数
async string FuncAsync()
{
string x = await XAsync();
string y = await YAsync();
string z = await ZAsync(); return x + y + z;
}
假设 XAsync(), YAsync(), YAsync()内部都Sleep10s, 则FuncAync()还是耗时多少呢?
整个FuncAsync会被拆成如下部分:
- FuncAsync-开始部分();
- XAsync();
- FuncAsync-中间部分1();
- YAsync();
- FuncAsync-中间部分2();
- ZAsync();
- FuncAsync-结束部分();
这些部分会被.NET同步上下文SynchronizationContext在同一个线程依次执行,如下:
- 同步上下文执行 FuncAsync-开始部分();
- 同步上下文执行 XAsync(); //内部调用了Sleep10s, 要10s才返回
- 同步上下文执行 FuncAsync-中间部分1();
- 同步上下文执行 YAsync();//内部调用了Sleep10s, 要10s才返回
- 同步上下文执行 FuncAsync-中间部分2()
- 同步上下文执行 ZAsync();//内部调用了Sleep10s, 要10s才返回
- 同步上下文执行 FuncAsync-结束部分();
所以耗时时间还是30s
假设 XAsync(), YAsync(), YAsync()内部都把Sleep10s用Task.Run()都包起来返回
- 同步上下文执行 FuncAsync-开始部分();
- 同步上下文执行 XAsync(); 立刻返回, 在另一个线程执行Sleep(10)
- 同步上下文等待 但可以执行其他异步任务, 如果是Winform的话UI不会卡
- 后台线程10s结束告诉同步上下文继续执行
- 同步上下文执行 FuncAsync-中间部分1();
- 同步上下文执行 YAsync(); 立刻返回, 在另一个线程执行Sleep(10)
- 同步上下文等待 但可以执行其他异步任务, 如果是Winform的话UI不会卡
- 后台线程10s结束告诉同步上下文继续执行
- 同步上下文执行 FuncAsync-中间部分2()
- 同步上下文执行 ZAsync(); 立刻返回, 在另一个线程执行Sleep(10)
- 同步上下文等待 但可以执行其他异步任务, 如果是Winform的话UI不会卡
- 后台线程10s结束告诉同步上下文继续执行
- 同步上下文执行 FuncAsync-结束部分();
所以FuncAsync总耗时时间还是30s,优点是UI线程不会被卡住, UI线程不是被FuncAsync一个任务占有,其他异步任务也能在UI线程上执行,真正等待的任务都运行在后台线程。
下面这个版本,FuncAsync()执行的时间才是10s
async string FuncAsync()
{
var taskX = XAsync();
var taskY = YAsync();
var taskZ = ZAsync(); await Task.WhenAll(taskX, taskY, taskZ); return taskX.Result + taskY.Result + taskZ.Result;
}
如果在XAsync()里调用Task.Result同步等待会怎样?
- 同步上下文执行 FuncAsync-开始部分();
- 同步上下文执行 XAsync();
- XAsync() 调用 YAsync() 返回task
- YAsync() 开启后台线程sleep10s
- XAsync()等待task.Result
- XAsync() 调用 YAsync() 返回task
- 后台线程10s结束告诉同步上下文继续
XAsync要等待的结果(task.Result),需要同步上下文返回,
虽然后台线程已经同步上下文结果了,但同步上下文此时却还在执行XAsync的过程中.
互相等待,于是产生死锁.
举个形象的例子:
- 医生Doctor给病人Patients看病, 病人必须先排队,按次序来。
- 轮到某个病人PatientX时要某项检查报告,病人说,等我一下,检查报告在我老婆手里,我打电话让她送来。
- 于是医生Doctor和病人PatientX开始等待, 左等右等不来。
- 其实几分钟后病人的老婆PatientXWife已经把检查报告送来了, 只不过她拿着检查报告在队列里等。