はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】SemaphoreSlimを用いて複数のスレッドでの処理を同時に開始させる方法(テストにも便利)

はじめに

今回はSemaphoreSlimというリソースに同時にアクセスできるスレッドの数を制限できるクラスについて紹介したいと思います。

learn.microsoft.com

概要

SemaphoreSlimリソースに同時にアクセスできるスレッド数を制限することができます。例えばテストコードで、同時アクセスした場合にのみ不具合が起きることの確認に使えたりできます。

リソースまたはリソースのプールに同時にアクセスできるスレッドの数を制限する Semaphore の軽量版を表します。

SemaphoreSlim クラス (System.Threading) | Microsoft Learn

使い方

SemaphoreSlim.WaitSemaphore.WaitAsyncで各スレッドの処理をブロックし、Semaphore.Releaseを実行して処理を開始させます。

// Dictionaryはスレッドセーフではない
Dictionary<string, int> dictionary = new Dictionary<string, int>();

const int Count = 4;
var tks = new Task[Count];

// 同時に許可される要求の初期数が0、最大数がCount(=4)に設定する
var semaphore = new SemaphoreSlim(0, Count);
            
// Dictionaryにて同時アクセスするとエラーを出力されることを確認する
// SemaphoreSlimを利用してヒット率を上げている
tks[0] = Task.Run(() =>
{
    semaphore.Wait();
    dictionary["Sample"] = 0;
});
tks[1] = Task.Run(() =>
{
    semaphore.Wait();
    dictionary["Sample"] = 1;
});
tks[2] = Task.Run(() =>
{
    semaphore.Wait();
    dictionary["Sample"] = 2;
});
tks[3] = Task.Run(() =>
{
    semaphore.Wait();
    dictionary["Sample"] = 3;
});
            
// semaphore.Waitまで辿り着くまで待つ
Thread.Sleep(500);

// dictionaryへのアクセスを全てのスレッドで開始する
semaphore.Release(Count);

// 全てのTaskが終わるまで待つ
// 同時アクセスした場合 : Unhandled exception. System.AggregateException: One or more errors occurred. (Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.) ...
Task.WaitAll(tks);

タイムアウトやCancellationToken

SemaphoreSlim.Waitの引数にタイムアウト時間やCancellationTokenを渡したりもできます。

  • Wait(TimeSpan, CancellationToken)
  • Wait(Int32, CancellationToken)
  • Wait(TimeSpan)
  • Wait(Int32)
  • Wait()
  • Wait(CancellationToken)
  • WaitAsync()
  • WaitAsync(CancellationToken)
  • WaitAsync(Int32)
  • WaitAsync(Int32, CancellationToken)
  • WaitAsync(TimeSpan)
  • WaitAsync(TimeSpan, CancellationToken)

SemaphoreSlim.Wait メソッド (System.Threading) | Microsoft Learn