文章

如何实现一个调度器?

Interview

手写一个调度器

如何实现一个调度器?

忽然发现近一个月没有更新博客了,难道是太忙了?可能是吧,每天都在赶需求,外加备战秋招,没有面试的时候,就做做测评,好像时间所剩无几了… 是啊,时间真快啊~ 转眼间已经实习了近 3 个月了,虽然累但逐渐喜欢上了目前的生活节奏,因为这里有充分的自由时间来做自己喜欢的事情,这 3 个月里,收获了太多,以至于像个老油条一样了…

实习期间,面了几家提前批,突然就很享受这种面试状态,在面试复盘中,我能够回到当初面试时的感觉,能够回忆出面试官的问题以及我的回答,这就导致我每次面试完都能够准确无误地梳理出每个问题,以及针对性地解决那些不会或答的不好的问题。虽然将这些问题解决后梳理在了思维导图上,但难以详细地去解释为什么(因为篇幅有限),并且思维导图的记录也只是某个点,无法扩散到面。所以,想了想还是应该开个分类去记录以及长篇大论一下原因,这样如果看思维导图时,某个点想不起来了就可以去对应的文章中去看。

题目

  • 实现:并发数为 2 的 scheduler 调度算法,你需要实现一个 addTask 函数,以实现以下输出

    addTask(1000, "1")
    
    addTask(500, "2")
    
    addTask(300, "3")
    
    addTask(400, "4")
    
  • 开始时,执行两个任务 1、2

  • 500ms 时,2 执行完毕,3 加入

  • 800ms 时,3 执行完毕,4 加入

  • 1000ms 时,1 执行完毕

  • 1400ms 时,4 执行完毕

  • 输出:2 - 3 - 1 - 4

解题思路

观察题目,“并发”、“调度算法”两个词异常亮眼,那么它们是什么?

什么是并发?

并发是指系统中多个任务或操作在同一时间段内执行

那么题目中的并发数指定了在同一时间段内,有相应数量的任务或操作执行

什么是调度算法?

调度算法指用于决定任务或作业执行顺序和资源分配

调度算法有哪些?

  • 先来先服务(FCFS):按照任务到达的顺序进行调度,先到达的任务先执行
  • 最短作业优先(SJF):在每个时间点选择执行时间最短的任务进行调度
  • 优先级调度:为每个任务分配优先级,并按照优先级调用任务
  • 时间片轮转:将时间划分为固定的时间片,每个任务在一个时间片内执行,然后切换到下一个任务
  • 多级反馈队列:将任务划分为不同的队列,每个队列具有不同的优先级和调度策略,任务根据优先级和队列的规则进行调度

那么题目中的调度算法属于哪一种?观察输出结果,能看出时间最短的先调度,故属于最短作业优先(SJF)

实现

class TaskScheduler {
  // 最大并发数
  maxConcurrency: number;
  // 任务队列
  tasks: Array<{ delay: number; taskName: string }>;
  // 正在执行的任务数
  runningTasks: number;
 
  constructor(maxConcurrency: number) {
    // 初始化
    this.maxConcurrency = maxConcurrency;
    this.tasks = [];
    this.runningTasks = 0;
  }
 
  // 添加任务
  addTask(delay: number, taskName: string): void {
    this.tasks.push({ delay, taskName });
    this.scheduleTasks();
  }
 
  // 执行任务
  async executeTask(task: { delay: number; taskName: string }): Promise<void> {
    await new Promise((resolve) => setTimeout(resolve, task.delay));
    console.log(task.taskName);
    this.runningTasks -= 1;
    this.scheduleTasks();
  }
 
  // 调度任务
  scheduleTasks(): void {
    while (this.runningTasks < this.maxConcurrency && this.tasks.length > 0) {
      // 取出任务(用数组模拟队列)
      const task = this.tasks.shift();
      if (task) {
        this.runningTasks += 1;
        this.executeTask(task);
      }
    }
  }
}
 
const scheduler = new TaskScheduler(2);
scheduler.addTask(1000, "1");
scheduler.addTask(500, "2");
scheduler.addTask(300, "3");
scheduler.addTask(400, "4");