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

5ssjco0h  于 2023-01-24  发布在  Java
关注(0)|答案(3)|浏览(250)

不完全是严肃的问题,更像是淋浴的想法: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();

这是一个正确的实现吗(除了正确的错误处理之外)?还有......我可以使用C++-RAII样式的锁保护吗?

shstlldc

shstlldc1#

您的实现允许与请求锁的数量一样多的使用者获得锁;对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;arrow函数 * 关闭 * 创建它们的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;
    };
}

示例:
一个二个一个一个

z31licg0

z31licg02#

这是正确的实现吗?
不,如果两个任务(我不能说是“线程”)试图在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");
7ivaypg9

7ivaypg93#

我建议使用async-mutex这样的库:

const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
    // ...
} finally {
    release();
}

相关问题