参考
この記事は、以下の動画を参考にしています。
詳しくは、動画をご覧ください。
リファレンス
非同期手続きの中ではlock文が使えない
async
を使った非同期手続きの中で、await
を付けた非同期呼び出しを含む文ブロックを、lock
文を使ってロックを掛けようとすると、ビルドエラーになる。
private static readonly Lock _lock = new();
public async Task DoWorkAsync()
{
lock (_lock)
{
//
await DoSubWorkAsync(); // ビルドエラー
//
}
}
lock
文を使わず、System.Threading.Lock
クラスのメソッドを使い、ロックの取得と解放をすると、ビルドエラーにはならない。
しかし、Lock
クラスのロックはスレッドに掛けられるため、await
の前後が異なるスレッドで実行される可能性のある非同期手続きでは、正常にロックが動作しない。(参照)
private static readonly Lock _lock = new();
public async Task DoWorkAsync()
{
_lock.Enter(); // ここを実行するスレッドと…
try
{
//
await DoSubWorkAsync(); // ビルドエラーにはならない
//
}
finally
{
_lock.Exit(); // ここを実行するスレッドが、異なる可能性がある
}
}
セマフォを使ってロックを掛ける
そこで非同期手続きでは、System.Threading.SemaphoreSlim
クラスのオブジェクトを用いて、ロックを掛けるとよい。
private static readonly SemaphoreSlim _semaphore = new(initialCount: 1, maxCount: 1);
public async Task DoWorkAsync()
{
await _semaphore.WaitAsync(); // ロックを掛ける
try
{
//
await DoSubWorkAsync();
//
}
finally
{
_semaphore.Release(); // ロックを解放する
}
}
ロックを掛ける際、タイムアウトを指定することもできる。
var locked = await _semaphore.WaitAsync(timeout: TimeSpan.FromSeconds(1));