功能说明
该示例把并发上限显式化。acquire 在有空闲许可时立即返回一个释放函数,否则返回一个 promise 直到有许可被释放。run 封装了获取、执行与释放,并用 finally 保证即使任务抛错也会释放,避免许可泄漏导致后续任务永久等待。
关键代码
等待者存放在队列里,release 时先递减活跃计数,再唤醒队首等待者。唤醒时直接把它计入活跃并把释放函数交给它,保证许可在交接期间不被重复使用。release 在无活跃许可时抛错,暴露重复释放或缺少 acquire 的编程错误。等待者以先进先出被唤醒,但没有严格公平保证,重负载下顺序可能受调度影响。
使用注意
许可上限应设为被保护资源的真实容量,如数据库连接数或下游配额,设得过高会让限制形同虚设。该信号量只限制并发,不提供超时;在资源长期饱和时应为 acquire 增加超时或取消,否则调用方可能无限等待。它适用于进程内协调,跨进程需借助集中式限流或队列。务必通过 run 或 finally 释放,任何漏释放都会永久削减可用许可。
代码示例
export class Semaphore {
constructor(limit) {
if (!Number.isInteger(limit) || limit <= 0) throw new RangeError('limit must be a positive integer');
this.limit = limit;
this.active = 0;
this.waiters = [];
}
async acquire() {
if (this.active < this.limit) {
this.active += 1;
return () => this.release();
}
await new Promise((resolve) => this.waiters.push(resolve));
this.active += 1;
return () => this.release();
}
release() {
if (this.active === 0) throw new Error('release without acquire');
this.active -= 1;
const next = this.waiters.shift();
if (next) {
this.active += 1;
next(() => this.release());
}
}
async run(task) {
const release = await this.acquire();
try {
return await task();
} finally {
release();
}
}
}