从外部函数停止 lua 协程以供调度使用

这个话题以前已经讨论过了,似乎外部只能关闭协程。当然,这对于调度器来说并不现实。有没有办法完全从外部暂停协程或者可能的绕过方法呢?

点赞
用户805875
用户805875

你可以使用 C API 设置钩子,在 $n 条 / 指令之后执行 yield。(不能通过 debug.sethook 实现,因为它添加了一层中间层,导致无法正常工作。)

你可以将其封装成一个单一的函数,然后将其公开给 Lua,除了添加一个函数外,你可以从 Lua 中完成所有操作。示例:

static int setyieldhook( lua_State * L ) {
   lua_State * coro;
   int steps;
   luaL_checktype( L, 1, LUA_TTHREAD );
   coro = lua_tothread( L, 1 );
   steps = luaL_optinteger( L, 2, 0 );
   if (steps <= 0) {
      lua_sethook( coro, NULL, 0, 0 );
   } else {
      lua_sethook( coro, yieldhook, LUA_MASKCOUNT, steps );
   }
   return 0;
}

然后将其作为一个函数推送到 Lua,并给其命名,例如:debug.setyieldhook

使用方式为 debug.setyieldhook( coro, timeout ),每当 coroutine 运行时,它将在 timeout Lua 指令之后执行 yield。清除钩子的方法为 debug.setyieldhook( coro, 0 )。(注意:你不能通过 debug.sethook 更改 / 移除通过 setyieldhook 设置的钩子,反之亦然 - 会抛出错误或默默引发问题。但是,你可以扩展 setyieldhook 来检测和清除“正常”的 Lua 钩子,并/或者包装 debug.sethook 以检测并清除 yield 钩子。)

其他需要注意的事项:

  • 如果 coroutine 调用了 yield,这将不会重置钩子计时器。
  • coroutine 将会执行 yield 但不会返回任何东西,因此你可能需要包装 coroutine.yield 和/或 coroutine.resume,以便区分“正常”的 yield 和超时 yield
  • C 函数不会记录指令计数,因此不会触发钩子(例如通过 string.* 进行长时间的非贪婪字符串匹配),因此无法提供严格的定时保证。
2017-07-02 20:07:24