Boost Asio:post传入的Executor与CompletionToken关联Executor的区别
Boost.Asio的post、dispatch这类函数,存在仅接收CompletionToken的重载,也有额外接收Executor的重载。已知不带Executor的重载,等效于传入CompletionToken关联的Executor的重载,但如果传入的Executor和CompletionToken关联的Executor不一致时,会产生什么效果?这两个Executor各自的作用是什么?CompletionToken的处理器最终会在哪个Executor上执行?
我测试发现以下代码中的断言会成功,这和我原本以为CompletionToken的处理器只会在单个Executor(比如strand)上执行的假设矛盾:
boost::asio::io_context ioc; auto ioc_ex = ioc.get_executor(); auto strand_a = boost::asio::make_strand(ioc_ex); auto strand_b = boost::asio::make_strand(ioc_ex); boost::asio::post(strand_a, boost::asio::bind_executor(strand_b, [&]() { assert(strand_a.running_in_this_thread() && strand_b.running_in_this_thread()); })); ioc.run();
之前看到有回答说,post传入的Executor是CompletionToken没有关联Executor时的备选执行器,但这显然不是它的唯一作用——否则上面代码中绑定到strand_b的CompletionToken应该会忽略strand_a才对,但实际并非如此。
核心原理拆解
Boost.Asio中,调用post(ex, token)这类函数时,执行流程分为两个关键阶段:
- 提交阶段:传入的Executor(示例中的strand_a)负责将任务提交到异步执行队列,这个过程严格遵循该Executor的调度规则(比如strand的串行化要求),它是任务进入异步系统的"入口"。
- 执行阶段:当任务被调度到执行时,会先进入CompletionToken绑定的Executor(示例中的strand_b)的上下文,再执行最终的处理器逻辑。
示例中两个断言都成立的原因:
由于strand_a和strand_b都基于同一个io_context,当任务通过strand_a进入执行上下文后,调用strand_b的调度逻辑时,当前线程已经处于io_context的线程中,strand的锁可以直接获取,因此会同时进入strand_b的上下文,两个running_in_this_thread()都会返回true。
两个Executor的各自作用
- 传入的Executor:管控任务的提交时机与提交过程的调度约束,确保任务按照该执行器的规则进入异步队列。
- CompletionToken关联的Executor:管控处理器逻辑的执行上下文,确保最终的代码在该执行器的约束下运行(比如串行化)。
处理器的执行上下文
CompletionToken的处理器会依次经过传入的Executor和关联的Executor的上下文,最终在关联的Executor上下文中完成执行。如果两个执行器的上下文可以兼容(如同属一个io_context的strand),就会出现示例中同时满足两个strand的running_in_this_thread()的情况。
内容的提问来源于stack exchange,提问作者Reizo

