みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

JavaScript で排他処理

Node.js はシングルスレッドで実行されるので,排他処理は原則不要ですが,非同期関数が含まれるとその限りではないと思ったので,排他処理ができるライブラリを探したのですが,自分で作った方が簡単だと思ったので,実験してみました.

以下のソースコードは全て TypeScript の Vitest 用単体テストです.

排他処理が必要になる例として,変数の値を取得して,非同期的に更新する操作を実装します.

lock.spec.ts

import { assert, test } from "vitest";

test("once", async () => {
    let v = 0;
    const addOne = async () => {
        const t = v;
        await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms
        v = t + 1;
    };
    await addOne().catch(() => { });
    assert.equal(v, 1);
});

test("twice without lock", async () => {
    let v = 0;
    const addOne = async () => {
        const t = v;
        await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms
        v = t + 1;
    };
    await Promise.all([addOne(), addOne()]).catch(() => { });;
    assert.equal(v, 1);
});

test("twice with lock", async () => {
    let v = 0;
    let lock = false;
    const addOne = async () => {
        while (lock) {
            await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms
        }
        lock = true;
        const t = v;
        await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms
        v = t + 1;
        lock = false;
    };
    await Promise.all([addOne(), addOne()]).catch(() => { });
    assert.equal(v, 2);
});

更新処理を非同期で実行する前に lock 変数を確認して,false になるまでまち続ける動作です.

実行したところ,全て合格しました.

 ✓ tests/lock.spec.ts (3)
   ✓ once
   ✓ twice without lock
   ✓ twice with lock

 Test Files  1 passed (1)
      Tests  3 passed (3)
   Start at  21:06:50
   Duration  53ms

マルチスレッドならば上記の例では不足しますが,Node.js はシングルスレッドで実行されるので問題ないようです.