不同线程上执行await调用时的上下文行为与阻塞特性问询
该场景下
await的执行上下文本质 await的上下文捕获逻辑从来不是强制绑定“启动任务的原始线程”,它只会捕获await触发瞬间,当前线程关联的SynchronizationContext(同步上下文)- 针对
TaskCreationOptions.LongRunning启动的任务:CLR会为该任务分配一个独立的非线程池托管线程,这个线程从创建开始就不会绑定UI同步上下文、ASP.NET请求同步上下文这类带强制线程亲和性的自定义上下文,运行在该线程上的代码,SynchronizationContext.Current默认值为null - 当代码执行到
await OtherAction()时,由于当前不存在可捕获的自定义同步上下文,await不会强制把后续执行流程切回最初那个LongRunning专用独立线程。等OtherAction执行完成后,对应的后续续体逻辑会直接投递到线程池,由任意空闲的线程池线程执行 - 这里有个很容易踩的使用误区:一旦代码触发第一个await,最初为LongRunning任务单独开辟的独立线程就会直接终止,后续所有逻辑都会在线程池线程上执行,相当于为了启动一个异步流程,白白多分配了一个马上就会销毁的独立线程,完全浪费了LongRunning的设计初衷。
该场景是否等同于阻塞
- 这种执行模式完全不等同于阻塞,二者有本质的资源使用差异:
- 阻塞的核心特征是:当前执行线程会被持续占用,直到等待的操作完成,期间完全无法释放线程资源处理其他工作,哪怕等待的是纯IO类的无CPU操作也一样
- 这个场景下的await执行逻辑是:
- 当
OtherAction尚未完成时,最初分配的LongRunning专用独立线程会直接被释放、终止,不会被卡在这里空等IO或者其他异步操作的结果 - 等
OtherAction完成后,后续代码由普通线程池线程调度执行,全程没有任何线程被强制占用来等待异步操作的完成
- 当
- 只有两种情况会真的出现等效阻塞效果:
- 你手动在该LongRunning线程上通过
.Wait()、.Result这类同步方法阻塞等待异步任务完成 - 你给该线程手动绑定了自定义的同步上下文,强制await把续体切回这个独立线程执行
- 你手动在该LongRunning线程上通过
内容的提问来源于stack exchange,提问作者CodeMonkey
相关产品推荐
相关产品推荐

