正在查看旧版本。 查看 当前版本.

与当前比较 查看页面历史记录

版本 1 下一个 »

Scheduler模块,实现了一个N-M协程调度器,也就是N个线程运行M个协程,协程可以在线程之间进行切换,也可以在某个指定的线程上运行。

调度器本质上就是内部维护一个协程任务队列,然后N个线程组成的线程池各自从这个任务队列中取协程任务并运行。N最少为1,也就是至少要有一个调度线程。然而比较特殊的一点是这里调度器的调度线程不一定要新创建,因为调度器所在的线程也可以加入调度,用于运行协程任务。比如常见的,main函数所在的线程就可以进行协程调度。

讨论几种情况:

  1. 调度器的退出问题,调度器内部有一个协程任务队列,调度器调度的实质就是内部的线程池从这个任务队列拿任务并执行,那么,停止调度该怎么实现?这里可以简化处理,强制规定只有所有的任务都完成调度时,调度器才可以退出,如果有一个任务没有执行完,那调度器就不能退出。
  2. 协程执行过程中主动调用yield让出了执行权,调度器要怎么处理?半路yield的协程显然并没有执行完,如果不处理这种协程,调度器就无法停止。一种处理方法是调度器在协程yield之后主动将协程再次加入调度,这样这个执行到半路的协程最终还是会被调度到,但这种策略是画蛇添足的,从生活的角度来比喻,一个成熟的协程是要学会自我管理的,既然你自己yield了,那么你就应该自己管理好自己,而不是让别人来帮你,这样才是一个成熟的协程。对于主动yield的协程,我们的策略是,由协程自己主动将自己加入调度,而不是由调度器来主动调度。这里规定了一点,协程在执行yield前,必须以某种形式先将自己重新添加到调度任务中。如果协程不顾后果地执行yield,最后的后果就是协程将永远无法再被执行,也就是所说的逃逸状态。
  3. 只使用调度器所在的线程进行调度,典型的就是main函数中定义调度器并且只使用main函数线程执行调度任务。这种场景下,可以认为是main函数先攒下一波协程,然后用协程的手段进入一个while(1)循环,再依次把这些协程消耗完。每个协程在运行时也可以继续创建新的协程并加入调度。如果所有协程都调度完了,并且没有创建新的调度任务,那么下一步就是讨论idle该如何处理。
  4. idle如何处理,也就是当调度器没有协程可调度时,调度线程该怎么办。直觉上来看这里应该有一些同步手段,比如,没有调度任务时,调度线程阻塞住,比如阻塞在一个idle协程上,等待新任务加入后退出idle协程,恢复调度。然而这种方案是无法实现的,因为单线程下,同一时间只能有一个协程在执行,如果调度器阻塞在idle协程上,那么除非idle协程自行让出执行权,否则其他的协程都得不到执行,这里就造成了一个先有鸡还是先有蛋的问题:只有创建新任务idle协程才会退出,只有idle协程退出才能创建新任务。所以,对于调度器来说,处理idle的方法简单而粗暴,如果任务队列空了,那就不停地看有没有新任务,俗称忙等待,CPU爆表。这个问题在sylar框架内无解,只有一种方法可以规避掉,那就是设置autostop标志,这个标志会使得调度器在调度完所有任务后自动退出。
  • 无标签