Node.js:如何实现简易Mutex机制规避竞态条件
解决Task类的并发竞态问题
你的核心问题是原实现中_busy布尔值的检查与锁定操作无法可靠保证排他性,在多异步回调并发触发run时,会出现多个调用绕过检查的情况。以下是两个无需依赖第三方库的简单解决方案:
方案一:用Promise跟踪执行状态
通过维护当前执行任务的Promise实例替代布尔锁,确保检查与锁定的原子性:
class Task { _cb; _currentTask = null; // 跟踪当前正在执行的任务 constructor(cb) { this._cb = cb; } async run(params = []) { // 已有任务在执行时直接返回 if (this._currentTask) { return; } try { // 创建任务Promise并标记锁定状态 this._currentTask = (async () => { await this._cb(...params); })(); await this._currentTask; } catch (err) { await someLoggingRoutine(); } finally { // 任务完成后解锁 this._currentTask = null; } } }
该方案利用Promise赋值的同步原子性,一旦第一个调用设置了_currentTask,后续所有调用都会直接返回,彻底避免竞态。
方案二:优化布尔锁的原子性逻辑
如果偏好布尔值锁的方式,可调整逻辑确保检查与锁定的连续性:
class Task { _cb; _locked = false; constructor(cb) { this._cb = cb; } async run(params = []) { // 原子性检查并锁定:通过检查后立即标记为锁定 if (this._locked) return; this._locked = true; try { await this._cb(...params); } catch (err) { await someLoggingRoutine(); } finally { // 无论成功失败都必须解锁,避免永久锁定 this._locked = false; } } }
原方案失效原因
原代码中Race condition!日志出现的本质是:当多个run调用的同步逻辑在同一事件循环周期内连续执行时,第一个调用还未完成this._busy = true的赋值,第二个调用就已经通过了if (this._busy)检查——这是因为异步回调的任务调度特性,导致多个同步逻辑块被快速连续执行,突破了布尔锁的防护。
上述两个方案都能确保同一时间只有一个run调用执行目标回调,其余调用直接返回,完全满足你的排他执行需求。
内容的提问来源于stack exchange,提问作者goodUser
相关产品推荐
相关产品推荐

