JavaScript 中使用锁的场景
·
JavaScript 中使用锁的场景
在 JavaScript 中,由于单线程特性,通常不需要传统多线程环境中的锁机制。但在异步编程、Web Workers 和某些并发场景下,仍然需要类似的同步机制。以下是常见的使用场景:
1. 异步操作竞态条件
当多个异步操作可能同时修改共享状态时:
class AsyncLock {
constructor() {
this.locked = false;
this.queue = [];
}
acquire() {
return new Promise(resolve => {
if (!this.locked) {
this.locked = true;
resolve();
} else {
this.queue.push(resolve);
}
});
}
release() {
if (this.queue.length > 0) {
const next = this.queue.shift();
next();
} else {
this.locked = false;
}
}
}
// 使用示例
const lock = new AsyncLock();
let sharedResource = 0;
async function criticalSection() {
await lock.acquire();
try {
// 对共享资源的操作
sharedResource++;
console.log('Modified shared resource:', sharedResource);
} finally {
lock.release();
}
}
// 多个异步操作同时调用
criticalSection();
criticalSection();
2. Web Workers 间的数据同步
当多个 Web Workers 需要访问共享数据时:
// 主线程
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// 创建锁 (基于 Atomics)
const lock = new Int32Array(sharedBuffer, 0, 1);
// Worker 中使用
function workerFunction() {
// 获取锁
while (Atomics.compareExchange(lock, 0, 0, 1) !== 0) {
// 等待锁释放
Atomics.wait(lock, 0, 1);
}
try {
// 临界区代码
console.log('Worker acquired lock');
} finally {
// 释放锁
Atomics.store(lock, 0, 0);
Atomics.notify(lock, 0, 1);
}
}
3. 防止重复请求/操作
在用户界面中防止重复点击或提交:
function createButtonLock() {
let isLocked = false;
return async function lockedAction(action) {
if (isLocked) {
console.log('Action is already in progress');
return;
}
isLocked = true;
try {
await action();
} finally {
isLocked = false;
}
};
}
// 使用
const submitForm = createButtonLock();
button.addEventListener('click', () => {
submitForm(async () => {
// 表单提交逻辑
await fetch('/api/submit', { method: 'POST' });
});
});
4. 数据库操作序列化
确保数据库操作按顺序执行:
class DatabaseLock {
constructor() {
this.pendingOperations = new Map();
}
async lockFor(key, operation) {
if (this.pendingOperations.has(key)) {
// 等待现有操作完成
await this.pendingOperations.get(key);
}
const operationPromise = operation();
this.pendingOperations.set(key, operationPromise);
try {
return await operationPromise;
} finally {
this.pendingOperations.delete(key);
}
}
}
// 使用
const dbLock = new DatabaseLock();
async function updateUserProfile(userId, updates) {
return dbLock.lockFor(`user-${userId}`, async () => {
// 数据库操作
const user = await db.getUser(userId);
Object.assign(user, updates);
await db.saveUser(user);
return user;
});
}
5. 缓存更新同步
防止缓存击穿和重复更新:
class CacheWithLock {
constructor() {
this.cache = new Map();
this.locks = new Map();
}
async get(key, factory) {
if (this.cache.has(key)) {
return this.cache.get(key);
}
if (!this.locks.has(key)) {
this.locks.set(key, Promise.resolve().then(async () => {
const value = await factory();
this.cache.set(key, value);
this.locks.delete(key);
return value;
}));
}
return this.locks.get(key);
}
}
// 使用
const cache = new CacheWithLock();
async function getExpensiveData(key) {
return cache.get(key, async () => {
// 昂贵的计算或网络请求
const data = await fetchDataFromServer(key);
return data;
});
}
注意事项
- 避免死锁:确保锁总是被释放,使用 try-finally 结构
- 性能考虑:锁会降低并发性,只在必要时使用
- 超时机制:为锁操作添加超时,防止永久阻塞
- 浏览器兼容性:SharedArrayBuffer 和 Atomics API 在某些环境下可能受限
JavaScript 中的锁机制主要用于协调异步操作,而不是传统意义上的线程同步。在设计时应充分考虑 JavaScript 的事件循环特性。
更多推荐
所有评论(0)