worker 有时候会阻塞
czlc opened this issue · 12 comments
不确定是不是以下原因:
worker 正要 sleep 的时候,这时 scheduler 给它安排新的 job,可能会造成 service_ready 有值且 sleeping == 1 的情况, 之后也无法唤醒了。
debug_printf
打开调度过程的 log 后,能不能看出问题?
我增加了更严格的条件变量 https://github.com/cloudwu/ltask/tree/cond
因为家里没有 win 开发环境,这个分支的 win 版本没有测试和编译。你可以先看看,如果有编译问题,麻烦自己先解决一下。
master 上的版本,只要有一个 worker 醒着,系统就不会阻塞。因为当每个 worker 手头的事情完成后,如果没有后续 job ,会尝试偷别的 worker 的 job 来做。 https://github.com/cloudwu/ltask/blob/master/src/ltask.c#L363-L364
所以, sleep/wakeup 机制不需要特别严格。而只需要在最后一个 worker 打算 sleep 前多尝试一次即可。因为它是最后一个醒着的 worker ,二次尝试后,如果没有新的工作就真的没有了。(没有其它 worker 并发)
我暂时没看出以上机制的问题。
不过这套机制比较晦涩,所以我在 cond 分支上用了更严格的条件变量唤醒机制。只有真的 sleep ,才去唤醒。而如果唤醒,一定保证唤醒的 worker 后续至少尝试一次处理 job 。这样可以去掉关于最后一个 worker 的判断。
昨天出现阻塞的时候是 所有的 worker 都处于 sleep,且 service_ready 有值(也可能是我本地其它修改导致:( ),今天用新旧版本都没重现问题了。我想有没有可能是 weakup 和 sleep 的调用时序问题,它们在不同线程被调用,weakup 执行过后才进入 sleep 就出现如上问题?
还有个情况,就是我的 service 之间很少交互,但是有比较高频的 ltask.sleep(1),有没有可能所有的 workers 即将 worker_sleep() 的时候, timer 的 weakup 却先于它执行造成的呢?
我比较关心现在的版本出现过问题没?
正常跑很难重现,不过调试的时候把 worker 在 worker_sleep() 前冻结住,让 timer 走一轮,再放开 worker 就能重现
应该是没完全改对,看看最后一次提交的修改?
这里,当分配完任务后,设置 wakeup 标志(修复:不应依赖 sleeping )。这样在 sleep 前,如果有 wakeup 标志,就会再尝试一次,而不会立刻 sleep 。
只有当 wakeup 标记不存在,且前面没有发现任务,才会 sleep 。这里的 wakeup 和 sleeping 标记都是在互斥锁里设置的。
我去,我合并的版本不对,没有 int wakeup; 这个标记,现在看起来应该稳了。
设置 wakeup 标志(修复:不应依赖 sleeping )。这样在 sleep 前,如果有 wakeup 标志,就会再尝试一次,而不会立刻 sleep 。
有 1 个地方没太看懂,cond_wait 为何用 while(!c->flag) 而不是 if (!c->flag),我理解是 SleepConditionVariableCS 返回后,flag 只可能是 1。因为只有 cond_trigger_end 能唤醒它,此时如果又来了其它 wakeup,因为 sleeping 标记还是 1,所以也不会设置 c->flag 为 0。
这个是两个层次的东西,sleeping 是更高的层次的标记。flag 是下一个层次的封装。所以 flag 的逻辑不考虑 sleeping 的存在。
了解了,谢谢