字节面试题:实现任务调度器(Scheduler)

突然想起去年面试字节时的一道题,题面大概是这样:

  1. 实现一个任务调度器 scheduler
  2. 只能同时执行 n 个任务
  3. 当有 n 个任务正在执行时,后安排的任务要排队

当时很久没面试了,紧张到冒汗😓,只写出一半就被叫停了;

今天辗转反侧睡不着,突然想起这道题,就给他写了吧 :)

实现

大致思路是:

  1. 定义 Scheduler 类:指定同时执行的任务数 n
  2. schedule 方法:安排一个任务(task),如果执行中的达到 n 则入队,否则直接执行
  3. 包装器 wrapper包装 task,使执行 task 完毕后检查是否要出队,即执行新任务

伪码如下:

typescript 复制代码
class Scheduler {
  // 计数当前执行中的数量
  private runningTask: number
  // 最大并发
  private capacity: number
  // 等待队列
  private pendingQueue: (() => Promise<any>)[]

  constructor(capacity: number) {
    this.capacity = capacity
    this.runningTask = 0
    this.pendingQueue = []
  }

  private wrapTask<T>(task: () => Promise<T>): () => Promise<void> {
    return async () => {
      // 执行 task
      // 判断 pending 队列是否空
      // 否,出队下一个 task
      // 是,更新计数
    }
  }

  public schedule<T>(task: () => Promise<T>): void {
    // 包装 task,得到 wrappedTask
    // 判断计数是否达到 n
    // 否,则直接执行 wrappedTask
    // 是,则入队
  }
}

然后,让我们来实现它:

typescript 复制代码
class Scheduler {
  // 计数当前执行中的数量
  private runningTask: number
  // 最大并发
  private capacity: number
  // 等待队列
  private pendingQueue: (() => Promise<any>)[]

  constructor(capacity: number) {
    this.capacity = capacity
    this.runningTask = 0
    this.pendingQueue = []
  }

  private wrapTask<T>(task: () => Promise<T>): () => Promise<void> {
    return async () => {
      // 执行 task
      await task()
      // 判断 pending 队列是否空
      if (this.pendingQueue.length > 0) {
        // 否,出队下一个 task
        const nextWrappedTask = this.pendingQueue.shift()!
        nextWrappedTask()
      } else {
        // 是,更新计数
        this.runningTask--
      }
    }
  }

  public schedule<T>(task: () => Promise<T>): void {
    // 包装 task,得到 wrappedTask
    const wrappedTask = this.wrapTask(task)
    // 判断计数是否达到 n
    if (this.runningTask < this.capacity) {
      // 否,则直接执行 wrappedTask
      wrappedTask()
    } else {
      // 是,则入队
      this.pendingQueue.push(wrappedTask)
    }
  }
}

进阶

这里还可以进一步:要求 schedule 返回 Task 的执行结果,怎么写?

EZ,我们只要:schedule 返回一个 Promise,并在 wrappedTaskresolve 结果 就行了,代码如下:

PS: 这里总结一个小技巧"传递 resolve/reject 来实现异步返回值"

typescript 复制代码
class Scheduler {
  private runningTask: number
  private capacity: number
  private pendingQueue: (() => Promise<any>)[]

  constructor(capacity: number) {
    this.capacity = capacity
    this.runningTask = 0
    this.pendingQueue = []
  }

  private wrapTask<T>(task: () => Promise<T>, resolve: (value: T) => void, reject: (error: any) => void): () => Promise<void> {
    return async () => {
      try {
        const res = await task()
        resolve(res)
        const nextTask = this.pendingQueue.shift()
        if (nextTask) {
          nextTask()
        } else {
          this.runningTask--
        }
      } catch (e) {
        reject(e)
      }
    }
  }

  public async schedule<T>(task: () => Promise<T>): Promise<T> {
    return (new Promise<T>((resolve, reject) => {
      const wrappedTask = this.wrapTask(task, resolve, reject)
      if (this.runningTask < this.capacity) {
        this.runningTask++
        wrappedTask()
      } else {
        this.pendingQueue.push(wrappedTask)
      }
    }))
  }
}
相关推荐
叽哥31 分钟前
Flutter面试:Dart基础2
flutter·面试·dart
胡gh2 小时前
聊一聊构建工具:Vite和Webpack
面试·webpack·vite
胡gh2 小时前
如何聊懒加载,只说个懒可不行
前端·react.js·面试
濮水大叔2 小时前
这个Database Transaction功能多多,你用过吗?
typescript·node.js·nestjs
Ratten2 小时前
06.TypeScript 元组类型、枚举类型
typescript
汪子熙2 小时前
浏览器里出现 .angular/cache/19.2.6/abap_test/vite/deps 路径究竟说明了什么
前端·javascript·面试
Process3 小时前
面试官:Vue和React源码里用到了哪些设计模式?
前端·javascript·面试
Ratten4 小时前
04.TypeScript 函数类型
typescript