コンテンツにスキップ
asyncmux
日本語
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

手動ロック

asyncmux(this) と asyncmux.readonly(this) を使って任意の範囲をロックする方法と、AsyncmuxLock の解放ルールを説明します。

任意の範囲をロックする

メソッド全体ではなく、特定の範囲や条件を満たす場合だけロックしたいときは、asyncmux を関数として呼び出します。第 1 引数に渡したオブジェクトがロックの対象です。

const lock = await asyncmux(this); // 書き込みロック
const readLock = await asyncmux.readonly(this); // 読み取りロック

戻り値は AsyncmuxLock です。ロックを解放するまで、同じオブジェクトに対する競合するロック要求は待機します。

import { asyncmux } from "asyncmux";

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

class Runner {
  isOpen = true;

  async run(ms: number) {
    if (!this.isOpen) {
      return; // ロックを取得する前に抜けます。
    }

    using _ = await asyncmux(this);

    await sleep(ms);
  }
}

using で自動解放する

AsyncmuxLock は Symbol.dispose を実装しているため、using 構文を使うとスコープを抜けるときに自動で解放されます。

async function run() {
  using _ = await asyncmux(this);
  await sleep(10);
  // スコープを抜けると自動で解放されます。
}

release() で明示的に解放する

using が使えない場合は、release() を finally で呼び出してください。

const lock = await asyncmux(this);

try {
  await task();
} finally {
  lock.release();
}

解放のルール

  • release() を 2 回呼び出すと LockReleasedError が投げられます。
  • 解放済みのロックに対して Symbol.dispose(using による解放)が呼ばれても、エラーにはならず何も起こりません。
  • 解放処理が例外を投げた場合でも、ロックは解放済みとして扱われます。2 回目の release() は LockReleasedError になります。
  • released プロパティーで解放済みかどうかを確認できます。

再入の注意

関数 API は、デコレーターのようにロックのコンテキストを登録しません。そのため、デコレーターが付いたメソッドの実行中でない限り、同じオブジェクトのロックを再取得しても ReentrantLockError にはならず、デッドロックします。

// デッドロックする例:
const lock1 = await asyncmux(target);
const lock2 = await asyncmux(target); // 永遠に待機します。

@asyncmux や @asyncmux.readonly が付いたメソッドの実行中は、そのコンテキストが再入検出の対象になります。詳しくは並行性モデルを参照してください。

AbortSignal で中断する

第 2 引数に AbortSignal を渡すと、ロック待機を中断できます。詳しくは中断を参照してください。

using _ = await asyncmux(this, signal);

このページは役に立ちましたか?