CLASS 16 · 2026-04-08 · 14-16 并发

互斥

当异步抽象数据类型需要等待资源时,竞态条件和死锁就会悄悄潜入。互斥保证某段代码同一时刻只有一个计算在执行,是守护共享状态的关键机制。

图书馆示例

设想一个图书馆系统,支持 checkout(借书)和 checkin(还书)。当一本书被借走时,后来的读者必须等待它归还。这个场景把异步等待、竞态和互斥都集中到了一起。

同步 ADT

最朴素的实现是同步抽象数据类型checkout 要么立即成功,要么立即失败。它简单直接,但无法表达「书被借走时我愿意等」的需求。

异步方法

checkout 改成异步的:当书不可用时,它返回一个 Promise<void>,等书被归还时才完成。这让调用者不必阻塞,而是可以继续做别的事。

创建并持有 Promise

问题来了:checkout 需要返回一个 Promise,但此刻还没有书可以归还——Promise 要在未来某个时刻才被解决。为此我们需要一种「先创建、后解决」的工具。

Deferred<T>:一个辅助 ADT,把 Promise 的创建与解决分离。它暴露 promise(给客户端)和 resolve/reject(给未来触发者)。

现代 JavaScript 可以用 Promise.withResolvers() 直接获得一个 Deferred:

const { promise, resolve, reject } = Promise.withResolvers<void>();
// promise 返回给等待的读者,resolve 在书归还时调用

异步方法如何持有 Promise

图书馆为每本被借出的书维护一个等待队列。checkout 把一个 Deferred 推入队列;checkin 归还书时,从队列取出 Deferred 并 resolve 它。

class Library {
  private holds: Map<string, Deferred<void>[]> = new Map();

  async checkout(book: string): Promise<void> {
    if (this.isAvailable(book)) {
      this.markBorrowed(book);
      return; // 立即成功
    }
    const { promise, resolve } = Promise.withResolvers<void>();
    this.holds.get(book)!.push({ promise, resolve });
    return promise; // 等待归还
  }

  checkin(book: string): void {
    const queue = this.holds.get(book)!;
    const next = queue.shift();
    if (next) next.resolve();      // 唤醒等待者
    else this.markAvailable(book); // 书重新可借
  }
}

竞态条件

当读者想借多本书时,一个微妙的竞态出现了:如果先等第一本书、拿到后再借第二本,而另一个读者恰好在这时借走了第二本书,双方可能陷入互相等待。这就是「持有并等待」导致的竞态。

修复竞态

修复思路是尽快借到每一本书:不要等前一本借到再去借下一本,而是为每本书独立排队,一旦某本就绪就立即锁定它。

死锁

死锁(deadlock):并发模块互相等待对方持有的资源,导致所有相关方都无法继续执行。

死锁通常需要四个条件同时成立:互斥、持有并等待、不可抢占、循环等待。破坏其中任意一个,就能预防死锁。

交错与互斥

在 TypeScript/JavaScript 中,await 之间的代码是连续执行的,不会被其他任务交错。这给了我们一个天然的互斥区域:只要把关键操作放在同一个 await 区间内,它们就等价于原子执行。

互斥(mutual exclusion):保证某段区域同一时刻只有一个计算在执行。

重新设计 checkout

利用这一性质,我们可以先通过 await 排队等待每本书就绪,然后在一个无 await 的临界区里一次性借出所有书。这样既避免了竞态,又不会死锁。

其他互斥技术

互斥是一个普适问题,不同层次有不同的解法:

关键要点

  • Deferred把 Promise 的创建与解决分离,用于异步 ADT。
  • 竞态多本书借阅时「持有并等待」导致的时序问题。
  • 死锁互相等待导致所有方停滞,需破坏四条件之一。
  • 互斥await 之间代码天然互斥,可构造临界区。
  • 其他锁、互斥量、信号量、事务是不同层次的互斥手段。