JavaScript 中的互斥 - 这看起来是正确的实现吗?
Mutex in JavaScript - does this look like a correct implementation?
这不是一个完全严肃的问题,更像是一个淋浴的想法:JavaScript 的 await
关键字应该允许一些在你的平均 "concurrent language" 中感觉非常像互斥锁的东西.
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
// Lock
await mutex.lock();
// Unlock
mutex.unlock();
这是正确的实现吗(除了正确的错误处理)?还有……我可以有 C++-RAII 风格的锁守卫吗?
您的实现允许尽可能多的消费者获得锁;每次调用 lock
都会等待一个承诺:
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
const mutex = new Mutex();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("A got the lock");
})();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("B got the lock");
})();
您需要实现一个承诺队列,为每个锁定请求创建一个新承诺。
旁注:
new Promise(t => t())
可以写得更简单和地道 Promise.resolve()
:-)
- 如果您正在使用这样的箭头函数,则不需要
self
;箭头函数 关闭 创建它们的 this
(就像关闭变量一样)
unlock
可能有意义作为锁承诺的解析值,因此只有获得锁的代码才能释放它
像这样:
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
实例:
"use strict";
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
const rand = max => Math.floor(Math.random() * max);
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
const mutex = new Mutex();
function go(name) {
(async () => {
console.log(name + " random initial delay");
await delay(rand(50));
console.log(name + " requesting lock");
const unlock = await mutex.lock();
console.log(name + " got lock");
await delay(rand(1000));
console.log(name + " releasing lock");
unlock();
})();
}
go("A");
go("B");
go("C");
go("D");
.as-console-wrapper {
max-height: 100% !important;
}
Is this a correct implementation?
没有。如果两个任务(我不能说 "threads")在当前锁定时尝试执行 mutex.lock()
,它们将同时获得锁定。我怀疑这就是你想要的。
JS 中的互斥量实际上只是一个布尔标志 - 您检查它,在获取锁时设置它,在释放锁时清除它。在检查和获取之间没有特殊的竞争条件处理,因为您可以在单线程 JS 中同步进行,而不会受到任何其他线程的干扰。
然而,您似乎正在寻找的是一个 队列,即您可以安排自己获取锁并在上一个时(通过承诺)收到通知的东西锁定已释放。
我会用
class Mutex {
constructor() {
this._lock = null;
}
isLocked() {
return this._lock != null;
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return () => {
if (this._lock == lock) this._lock = null;
release();
};
}
acquireSync() {
if (this.isLocked()) throw new Error("still locked!");
return this._acquire();
}
acquireQueued() {
const q = Promise.resolve(this._lock).then(() => release);
const release = this._acquire(); // reserves the lock already, but it doesn't count
return q; // as acquired until the caller gets access to `release` through `q`
}
}
演示:
class Mutex {
constructor() {
this._lock = Promise.resolve();
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return release;
}
acquireQueued() {
const q = this._lock.then(() => release);
const release = this._acquire();
return q;
}
}
const delay = t => new Promise(resolve => setTimeout(resolve, t));
const mutex = new Mutex();
async function go(name) {
await delay(Math.random() * 500);
console.log(name + " requests lock");
const release = await mutex.acquireQueued();
console.log(name + " acquires lock");
await delay(Math.random() * 1000);
release()
console.log(name + " releases lock");
}
go("A");
go("B");
go("C");
go("D");
我建议使用 async-mutex:
这样的库
const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}
这不是一个完全严肃的问题,更像是一个淋浴的想法:JavaScript 的 await
关键字应该允许一些在你的平均 "concurrent language" 中感觉非常像互斥锁的东西.
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
// Lock
await mutex.lock();
// Unlock
mutex.unlock();
这是正确的实现吗(除了正确的错误处理)?还有……我可以有 C++-RAII 风格的锁守卫吗?
您的实现允许尽可能多的消费者获得锁;每次调用 lock
都会等待一个承诺:
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
const mutex = new Mutex();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("A got the lock");
})();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("B got the lock");
})();
您需要实现一个承诺队列,为每个锁定请求创建一个新承诺。
旁注:
new Promise(t => t())
可以写得更简单和地道Promise.resolve()
:-)- 如果您正在使用这样的箭头函数,则不需要
self
;箭头函数 关闭 创建它们的this
(就像关闭变量一样) unlock
可能有意义作为锁承诺的解析值,因此只有获得锁的代码才能释放它
像这样:
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
实例:
"use strict";
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
const rand = max => Math.floor(Math.random() * max);
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
const mutex = new Mutex();
function go(name) {
(async () => {
console.log(name + " random initial delay");
await delay(rand(50));
console.log(name + " requesting lock");
const unlock = await mutex.lock();
console.log(name + " got lock");
await delay(rand(1000));
console.log(name + " releasing lock");
unlock();
})();
}
go("A");
go("B");
go("C");
go("D");
.as-console-wrapper {
max-height: 100% !important;
}
Is this a correct implementation?
没有。如果两个任务(我不能说 "threads")在当前锁定时尝试执行 mutex.lock()
,它们将同时获得锁定。我怀疑这就是你想要的。
JS 中的互斥量实际上只是一个布尔标志 - 您检查它,在获取锁时设置它,在释放锁时清除它。在检查和获取之间没有特殊的竞争条件处理,因为您可以在单线程 JS 中同步进行,而不会受到任何其他线程的干扰。
然而,您似乎正在寻找的是一个 队列,即您可以安排自己获取锁并在上一个时(通过承诺)收到通知的东西锁定已释放。
我会用
class Mutex {
constructor() {
this._lock = null;
}
isLocked() {
return this._lock != null;
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return () => {
if (this._lock == lock) this._lock = null;
release();
};
}
acquireSync() {
if (this.isLocked()) throw new Error("still locked!");
return this._acquire();
}
acquireQueued() {
const q = Promise.resolve(this._lock).then(() => release);
const release = this._acquire(); // reserves the lock already, but it doesn't count
return q; // as acquired until the caller gets access to `release` through `q`
}
}
演示:
class Mutex {
constructor() {
this._lock = Promise.resolve();
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return release;
}
acquireQueued() {
const q = this._lock.then(() => release);
const release = this._acquire();
return q;
}
}
const delay = t => new Promise(resolve => setTimeout(resolve, t));
const mutex = new Mutex();
async function go(name) {
await delay(Math.random() * 500);
console.log(name + " requests lock");
const release = await mutex.acquireQueued();
console.log(name + " acquires lock");
await delay(Math.random() * 1000);
release()
console.log(name + " releases lock");
}
go("A");
go("B");
go("C");
go("D");
我建议使用 async-mutex:
这样的库const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}