981377660LMT / ts

ts学习
6 stars 1 forks source link

获取锁失败时的策略,有哪些 #614

Open 981377660LMT opened 1 month ago

981377660LMT commented 1 month ago

获取锁失败时的策略主要取决于应用场景和需求,但通常可以归纳为以下几种:

  1. 阻塞(Blocking):

    • 等待直到锁变为可用状态。这种策略适用于必须执行的操作,确保每个请求都将被处理,但可能会导致性能问题或死锁。
  2. 忽略(Ignoring):

    • 如果锁不可用,立即返回或丢弃请求。这适用于可以容忍丢失的操作,例如,快速连续的用户界面更新。
  3. 超时(Timeout):

    • 等待一定时间锁变为可用。如果在指定时间内锁没有变为可用,则放弃。这种策略是阻塞和忽略策略的折中方案,适用于既不能长时间等待也不能直接忽略的场景。
  4. 重试(Retrying):

    • 如果获取锁失败,则在一段时间后重试,可能会结合退避算法(例如,指数退避)来增加重试间隔。这种策略适用于临时的锁竞争情况。
  5. 回调(Callback):

    • 将请求放入队列,并在锁可用时通过回调函数执行。这种策略确保了请求的顺序执行,适用于需要维护操作顺序的场景。
  6. 优先级(Prioritization):

    • 根据请求的优先级决定是否获取锁。高优先级的请求可能会抢占低优先级的请求。这适用于有明确优先级需求的场景。
  7. 条件执行(Conditional Execution):

    • 只有满足特定条件时才尝试获取锁,否则跳过或延迟操作。这种策略适用于操作依赖于特定状态或条件的场景。

每种策略都有其适用场景和潜在的权衡。选择哪种策略应基于对性能、资源利用、用户体验和业务逻辑的综合考虑。在实际应用中,可能会根据具体需求组合使用多种策略。

981377660LMT commented 1 month ago
type AsyncFunction<T extends any[], R> = (...args: T) => Promise<R>

interface LockifyOptions {
  failureStrategy: 'block' | 'ignore'
}

function lockify<T extends any[], R>(fn: AsyncFunction<T, R>, options: LockifyOptions): AsyncFunction<T, R | void> {
  let locked = false
  const queue: Array<{ resolve: (value: R | void) => void; reject: (reason?: any) => void; args: T }> = []

  return async (...args: T): Promise<R | void> => {
    if (!locked) {
      return run(...args)
    }

    switch (options.failureStrategy) {
      case 'block':
        return new Promise((resolve, reject) => {
          queue.push({ resolve, reject, args })
        })
      case 'ignore':
        return
    }
  }

  async function run(...args: T): Promise<R> {
    locked = true
    try {
      return await fn(...args)
    } finally {
      locked = false
      processQueue()
    }
  }

  function processQueue(): void {
    if (!queue.length) return
    const { resolve, reject, args } = queue.shift()!
    run(...args)
      .then(resolve)
      .catch(reject)
  }
}

export {}

if (require.main === module) {
  // 使用示例
  async function fetchData(): Promise<string> {
    await new Promise(resolve => setTimeout(resolve, 2000))
    return '请求结果'
  }

  const blockingFetch = lockify(fetchData, { failureStrategy: 'block' })
  console.log('测试阻塞行为')
  blockingFetch().then(console.log)
  blockingFetch().then(console.log)
  blockingFetch().then(console.log)

  const ignoringFetch = lockify(fetchData, { failureStrategy: 'ignore' })
  console.log('测试忽略行为')
  ignoringFetch().then(console.log)
  ignoringFetch().then(console.log)
  ignoringFetch().then(console.log)
}