在最基本的层面上,scheduler 可以被建模为一个存放 task 的运行队列和一个消耗该队列的 processor。processor 是运行在线程上的一小段代码。

多线程模型

  • one queue, multi processors:通用线程池的实现
  • multi processors, each with their own queue:负载不均

工作窃取调度

runnable task 放到当前 processor 的运行队列。使用固定大小的 per-processor 运行队列,当队列满时放到固定大小的 spmc(single-producer multi-consumers)全局队列。

当 processor 空闲时随机选择同级 processor 窃取任务,若没取到再试下一个 processor。

下一运行槽

任务 A 发送消息唤醒 任务 B,任务 B 会放到当前 processor 的运行队列。等过段时间任务 B 实际运行时,发送时刻的”热”数据(cpu 缓存还有效,比如唤醒消息)可能已经失效。为了利用”热”数据,多加一个下一运行槽。插入时将任务插入下一运行槽;若下一运行槽已有任务,则先将已有任务移到运行队列,再将任务插入下一运行槽。使用时先取下一运行槽,再取运行队列

窃取节流

多个 processor 可能同时为空,同时窃取导致数据争用,需要限制窃取的并发 processor 数。

同级通知

  • 当有新任务时,若没有其他 processor 处于窃取状态,则唤醒睡眠的同级 processor。
  • processor 一被唤醒,立刻转入窃取状态。
  • 处于窃取状态的 processor 若收到/窃取到新任务,首先跳出窃取状态,然后尝试通知其他 processor。

参考