互斥
当异步抽象数据类型需要等待资源时,竞态条件和死锁就会悄悄潜入。互斥保证某段代码同一时刻只有一个计算在执行,是守护共享状态的关键机制。
图书馆示例
设想一个图书馆系统,支持 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 的临界区里一次性借出所有书。这样既避免了竞态,又不会死锁。
其他互斥技术
互斥是一个普适问题,不同层次有不同的解法:
- 抢占式并发使用锁(lock)、互斥量(mutex)、信号量(semaphore)。
- 数据库使用事务(transaction)保证一组操作的原子性。
- TypeScript 的单线程模型则让我们用
await之间的代码段天然实现互斥。
关键要点
- Deferred把 Promise 的创建与解决分离,用于异步 ADT。
- 竞态多本书借阅时「持有并等待」导致的时序问题。
- 死锁互相等待导致所有方停滞,需破坏四条件之一。
- 互斥await 之间代码天然互斥,可构造临界区。
- 其他锁、互斥量、信号量、事务是不同层次的互斥手段。