nodejs 并发执行 锁
在 Node.js 中处理并发任务时,可能会遇到多个异步操作同时访问共享资源的问题。为了避免数据竞争或不一致的状态,我们需要引入锁机制来控制并发。解决方案,并提供几种实现思路和代码示例。
解决方案
通过使用锁机制(如互斥锁、读写锁等),可以确保在同一时间只有一个任务能够访问共享资源。Node.js 中可以通过多种方式实现锁,例如基于 Promise 的同步锁、Redis 分布式锁、或者使用第三方库如 async-mutex
或 redlock
。
1. 基于 Promise 的简单锁
最简单的锁实现是通过一个标志位来控制任务的顺序执行。以下是一个基于 Promise 的锁实现:
javascript class SimpleLock { constructor() { this.isLocked = false; }</p> <p>acquire() { return new Promise((resolve) => { const checkAndResolve = () => { if (!this.isLocked) { this.isLocked = true; resolve(); } else { setTimeout(checkAndResolve, 10); // 每隔 10ms 检查一次锁状态 } }; checkAndResolve(); }); }</p> <p>release() { this.isLocked = false; } }</p> <p>// 示例:模拟并发任务 const lock = new SimpleLock();</p> <p>function task(name, delay) { return lock.acquire().then(() => { console.log(<code>${name} 开始
); return new Promise((resolve) => { setTimeout(() => { console.log(${name} 完成
); lock.release(); resolve(); }, delay); }); }); }Promise.all([ task('Task A', 2000), task('Task B', 1000), ]).then(() => { console.log('所有任务完成'); });
说明:
- acquire()
方法用于获取锁,只有当锁未被占用时才能继续执行。
- release()
方法用于释放锁,允许其他任务获取锁。
2. 使用 async-mutex 实现锁
async-mutex
是一个流行的第三方库,提供了更简洁的锁实现方式。以下是使用该库的示例:
javascript const { Mutex } = require('async-mutex');</p> <p>const mutex = new Mutex();</p> <p>function task(name, delay) { return mutex.runExclusive(async () => { console.log(<code>${name} 开始
); await new Promise((resolve) => setTimeout(resolve, delay)); console.log(${name} 完成
); }); }Promise.all([ task('Task A', 2000), task('Task B', 1000), ]).then(() => { console.log('所有任务完成'); });
说明:
- runExclusive()
方法会自动确保同一时间只有一个任务执行。
- 当前任务完成后,锁会自动释放。
3. Redis 分布式锁
在分布式系统中,多个进程可能需要协调对共享资源的访问。此时可以使用 Redis 实现分布式锁。以下是一个基于 redlock
库的示例:
javascript const Redlock = require('redlock'); const redis = require('redis');</p> <p>// 创建 Redis 客户端 const client = redis.createClient({ host: 'localhost', port: 6379, });</p> <p>// 初始化 Redlock const redlock = new Redlock([client], { driftFactor: 0.01, // 锁过期时间的漂移因子 retryCount: 10, // 尝试获取锁的次数 retryDelay: 200, // 每次尝试之间的延迟 });</p> <p>async function task(name, delay) { const lockKey = 'shared<em>resource</em>lock'; const lock = await redlock.lock(lockKey, 5000); // 锁定 5 秒</p> <p>try { console.log(<code>${name} 开始
); await new Promise((resolve) => setTimeout(resolve, delay)); console.log(${name} 完成
); } finally { await lock.unlock(); // 释放锁 } }Promise.all([ task('Task A', 2000), task('Task B', 1000), ]).then(() => { console.log('所有任务完成'); });
说明:
- redlock.lock()
方法用于获取分布式锁。
- 锁的过期时间应根据任务执行时间合理设置,避免死锁。
4. 其他思路:队列机制
除了锁机制,还可以通过队列的方式控制任务的并发执行。以下是一个基于队列的实现:
javascript
class TaskQueue {
constructor() {
this.queue = [];
this.isProcessing = false;
}</p>
<p>addTask(task) {
this.queue.push(task);
this.processNext();
}</p>
<p>async processNext() {
if (this.isProcessing || this.queue.length === 0) return;</p>
<pre><code>this.isProcessing = true;
const task = this.queue.shift();
try {
await task();
} finally {
this.isProcessing = false;
this.processNext();
}
}
}
const queue = new TaskQueue();
function createTask(name, delay) {
return async () => {
console.log(${name} 开始
);
await new Promise((resolve) => setTimeout(resolve, delay));
console.log(${name} 完成
);
};
}
queue.addTask(createTask('Task A', 2000));
queue.addTask(createTask('Task B', 1000));
setTimeout(() => {
console.log('所有任务完成');
}, 3000);
说明:
- 队列机制可以确保任务按顺序执行,避免并发问题。
- 适用于不需要复杂锁逻辑的场景。
几种在 Node.js 中实现并发控制的思路和代码示例:
1. 基于 Promise 的简单锁:适合单机环境下的轻量级任务。
2. 使用 async-mutex:提供了更简洁的 API 和更好的可维护性。
3. Redis 分布式锁:适用于分布式系统中的共享资源访问控制。
4. 队列机制:通过任务排队的方式避免并发冲突。
根据实际需求选择合适的方案,可以有效解决并发问题并提高系统的稳定性。