字节面试题:实现任务调度器(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)
      }
    }))
  }
}
相关推荐
@AfeiyuO2 小时前
分类别柱状图(Vue3)
typescript·vue·echarts
一只小风华~2 小时前
Vue:条件渲染 (Conditional Rendering)
前端·javascript·vue.js·typescript·前端框架
PAK向日葵3 小时前
【C/C++】面试官:手写一个memmove,要求性能尽可能高
c语言·c++·面试
moz与京7 小时前
【面试向】热门技术话题(上)
人工智能·物联网·机器学习·面试·web3·区块链·元宇宙
野犬寒鸦9 小时前
力扣hot100:环形链表(快慢指针法)(141)
java·数据结构·算法·leetcode·面试·职场和发展
顾林海9 小时前
探秘Android JVM TI:虚拟机背后的"隐形管家"
android·面试·性能优化
秃顶老男孩.10 小时前
异步处理(前端面试)
前端·面试·职场和发展
烛阴10 小时前
【TS 设计模式完全指南】用适配器模式优雅地“兼容”一切
javascript·设计模式·typescript
围巾哥萧尘11 小时前
普通人如何实现人生逆袭🧣
面试
本末倒置18311 小时前
前端面试高频题:18个经典技术难点深度解析与解决方案
前端·vue.js·面试