一个 cpp 协程库的前世今生(二十二)协程偷取
为了防止大家找不到代码,还是先贴一下项目链接:
GitHub - skyfireitdiy/cocpp at cocpp-0.1.0
前面的文章介绍了如何避免单个协程长时间占用 cpu 引起同一个调度线程上其他协程阻塞。
然而还有另一个问题需要解决,当一个调度线程上的协程全部完成后,这个线程应该怎么进行下一步动作呢?有几种选择:
销毁
休眠
去其他调度线程中“偷取”一个可移动的协程来运行
这三个处理方式各有优缺点。在 cocpp 中都有使用,其整体策略如下。
协程空闲时的策略
在 cocpp 中,会有两个阈值:基础 env 数量 base_env_count 和最大 env 数量 max_env_count。
在当前 env 数量小于 base_env_count 时,每当有新的 ctx 加入,都会新分配一个 env 来调度(因此当协程数量小于等于 base_env_count 时相当于多线程执行)。
当协程数量大于 base_env_count 而小于等于 max_env_count 时,如果有新的 ctx 加入,只有在当前协程都无法调度的时候才会创建新的 env,需要注意一点,在这个范围内的 env 不会自动被销毁
当协程数量大于 max_env_count 时,超过的部分空闲 ctx 会被回收。
这个策略还面临一个问题,在一个 env 中没有 ctx 可以被调度的时候,可能另外的 env 中积压了很多 ctx,此时如果直接休眠或者销毁,对整体调度的响应速度来说是不利的。
因此在 env 中没有可调度的 ctx 时,env 会尝试从其他的 env 中偷取一个 ctx 来执行,防止自身被销毁或者休眠。
协程偷取
协程偷取的流程位于定时任务中(source/cocpp/core/co_manager.cpp):
主要实现为 steal_ctx_routine__函数。
steal_ctx_routine__
steal_ctx_routine__函数实现如下(source/cocpp/core/co_manager.cpp):
其实现分为两个部分。
找出所有 env 中目前空闲的 env(这些是需要从其他 env 中偷取 ctx 的)。
遍历所有的 env,为每个空闲 env 偷取一个 ctx
整个流程的逻辑非常简单。这里仅看一下 take_one_movable_ctx 函数的实现(source/cocpp/core/co_env.cpp):
此函数与 take_all_movable_ctx 非常类似,不同点在于,本函数找到第一个可移动的 ctx 就会返回。
总结
本文主要介绍了 env 在空闲状态采取的策略,以及协程偷取的流程,还有很多不完善之处,待后续版本优化。
版权声明: 本文为 InfoQ 作者【SkyFire】的原创文章。
原文链接:【http://xie.infoq.cn/article/b36e646b1e11076c19c23d711】。文章转载请联系作者。
评论