JavaScript中的互斥体-这看起来像是正确的实现吗?

Cae*_*sar 6 javascript mutex

这不是一个完全严肃的问题,更多的是在想一想:JavaScript的await关键字应该允许您感觉像普通“并发语言”中的互斥锁一样可怕。

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();
Run Code Online (Sandbox Code Playgroud)

这是正确的实现吗(除了适当的错误处理)?而且...我可以拥有C ++-RAII风格的锁卫吗?

T.J*_*der 8

您的实现允许尽可能多的使用者获得所需的锁;每次调用都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");
})();
Run Code Online (Sandbox Code Playgroud)

您需要实现一个诺言队列,为每个锁定请求创建一个新的诺言。

旁注:

  • 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;
    };
}
Run Code Online (Sandbox Code Playgroud)

现场示例:

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;
    };
}
Run Code Online (Sandbox Code Playgroud)
"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");
Run Code Online (Sandbox Code Playgroud)

  • @Caesar - 不*完全*一样。`_resolve =resolve` 意味着传递到 `_resolve` 的任何参数都可以控制 Promise(设置一个解析值,甚至通过使用被拒绝的 Promise 调用它来导致拒绝 [或一个待处理并最终拒绝的 Promise])。`_resolve = () =>resolve()` 显式避免传递它可能接收的任何参数,并确保在不带参数的情况下调用 `resolve`(因此 Promise 以 `undefined` 解析)。 (4认同)

Ber*_*rgi 7

这是正确的实现吗?

不。如果两个任务(我不能说“线程”)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`
    }
}
Run Code Online (Sandbox Code Playgroud)

演示:

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`
    }
}
Run Code Online (Sandbox Code Playgroud)