C# の SemaphoreSlim クラス

C# の SemaphoreSlim クラスと例

この記事では、C# で SemaphoreSlim クラスを使用してスレッド同期を実装する方法について説明します。 例で。 C# でセマフォ クラスを使用してスレッド同期を実装する方法について説明した以前の記事をお読みください。 例で。 SemaphoreSlim クラスは、リソースまたはリソースのプールに同時にアクセスできるスレッドの数を制限する Semaphore の軽量な代替手段を表します。

C# には既に Lock、Monitor、Mutex、および Semaphore があるのに、なぜ SemaphoreSlim が必要なのですか?

ロック、モニター、ミューテックス、セマフォと同様 、C# の SemaphoreSlim クラスもスレッド セーフを提供するために使用されます。ロックとモニターは基本的に、内部スレッド、つまりアプリケーション自体によって生成されたスレッドにスレッド セーフを提供するために使用されます。一方、ミューテックスとセマフォは、外部アプリケーション、つまり外部スレッドによって生成されたスレッドのスレッドセーフを保証します。 Mutex を使用すると、任意の時点で 1 つの外部スレッドのみがアプリケーション コードにアクセスできます。また、アプリケーション コードにアクセスできる外部スレッドの数をさらに制御したい場合は、C# でセマフォを使用できます。

Lock and Monitor を使用すると、任意の時点で 1 つの内部スレッドだけがアプリケーション コードにアクセスできます。ただし、アプリケーション コードにアクセスできる内部スレッドの数をさらに制御したい場合は、C# で SemaphoreSlim クラスを使用する必要があります。理解を深めるために、下の画像をご覧ください。

C# の SemaphoreSlim クラスとは?

SemaphoreSlim クラスは、1 つのアプリ内での同期に推奨されるセマフォです。軽量セマフォは、アプリケーションに対してローカルなリソースのプールへのアクセスを制御します。リソースまたはリソースのプールに同時にアクセスできるスレッドの数を制限する、セマフォの軽量な代替手段を表します。

C# の SemaphoreSlim クラスのコンストラクタとメソッド:

C# の SemaphoreSlim クラスのさまざまなコンストラクターとメソッドを理解しましょう。 SemaphoreSlim クラスを右クリックして [定義に移動] を選択すると、次のクラス定義が表示されます。

C# の SemaphoreSlim クラスのコンストラクタ:

C# の SemaphoreSlim クラスは、SemaphoreSlim クラスのインスタンスを作成するために使用できる次の 2 つのコンストラクターを提供します。

  1. SemaphoreSlim(int initialCount): 同時に許可できる要求の初期数を指定して、SemaphoreSlim クラスの新しいインスタンスを初期化します。ここで、パラメーター initialCount は、同時に許可できるセマフォの要求の初期数を指定します。 initialCount が 0 未満の場合、ArgumentOutOfRangeException がスローされます。
  2. SemaphoreSlim(int initialCount, int maxCount): 同時に許可できる要求の初期数と最大数を指定して、SemaphoreSlim クラスの新しいインスタンスを初期化します。ここで、パラメーター initialCount は、同時に許可できるセマフォの要求の初期数を指定します。また、パラメーター maxCount は、同時に許可できるセマフォの最大要求数を指定します。 initialCount が 0 未満、initialCount が maxCount より大きい、または maxCount が 0 以下の場合、ArgumentOutOfRangeException がスローされます。
C# の SemaphoreSlim クラスのメソッド:

C# の SemaphoreSlim クラスは、次のメソッドを提供します。

待機方法:

SemaphoreSlim クラスで使用できる Wait メソッドのオーバーロードされたバージョンが複数あります。それらは次のとおりです:

  1. 待機(): System.Threading.SemaphoreSlim に入ることができるまで、現在のスレッドをブロックします。
  2. 待機 (TimeSpan タイムアウト): TimeSpan を使用してタイムアウトを指定し、SemaphoreSlim に入ることができるまで現在のスレッドをブロックします。現在のスレッドが正常に SemaphoreSlim に入った場合は true を返します。それ以外の場合は false。
  3. Wait(CancellationToken cancelToken): CancellationToken を監視しながら、SemaphoreSlim に入ることができるまで、現在のスレッドをブロックします。
  4. Wait(TimeSpan タイムアウト、CancellationToken cancelToken): CancellationToken を観察しながら、タイムアウトを指定する TimeSpan を使用して、SemaphoreSlim に入ることができるまで現在のスレッドをブロックします。現在のスレッドが正常に SemaphoreSlim に入った場合は true を返します。それ以外の場合は false。
  5. Wait(int millisecondsTimeout): タイムアウトを指定する 32 ビットの符号付き整数を使用して、SemaphoreSlim に入ることができるまで、現在のスレッドをブロックします。現在のスレッドが正常に SemaphoreSlim に入った場合は true を返します。それ以外の場合は false。
  6. Wait(int millisecondsTimeout, CancellationToken cancelToken): CancellationToken を観察しながら、タイムアウトを指定する 32 ビット符号付き整数を使用して、SemaphoreSlim に入ることができるまで現在のスレッドをブロックします。現在のスレッドが正常に SemaphoreSlim に入った場合は true を返します。それ以外の場合は false。
パラメータ:

Wait メソッドで使用されるパラメーターの説明は次のとおりです。

  1. タイムアウト: 待機するミリ秒数を表す TimeSpan、無期限に待機する -1 ミリ秒を表す TimeSpan、または待機ハンドルをテストしてすぐに戻る 0 ミリ秒を表す TimeSpan。
  2. cancellationToken :観察する System.Threading.CancellationToken。
  3. ミリ秒タイムアウト :待機するミリ秒数、無期限に待機する System.Threading.Timeout.Infinite(-1)、待機ハンドルの状態をテストしてすぐに戻るゼロ。

注: 上記のすべてのメソッドの非同期バージョンも利用できます。

解放方法:

SemaphoreSlim クラスで使用できるリリース メソッドには、オーバーロードされたバージョンが 2 つあります。それらは次のとおりです:

  1. リリース(): SemaphoreSlim オブジェクトを一度解放します。 SemaphoreSlim の以前のカウントを返します。
  2. リリース(int releaseCount): SemaphoreSlim オブジェクトを指定された回数解放します。 SemaphoreSlim の前のカウントを返します。ここで、パラメーター releaseCount は、セマフォを終了する回数を指定します。
C# での SemaphoreSlim の動作

セマフォをインスタンス化するとき、セマフォに同時に入ることができるスレッドの最大数を指定できます。また、セマフォに同時に入ることができるスレッドの初期数も指定します。これは、セマフォのカウントを定義します。カウントは、スレッドがセマフォに入るたびに減少し、スレッドがセマフォを解放するたびに増加します。

セマフォに入るには、スレッドは Wait または WaitAsync オーバーロードのいずれかを呼び出す必要があります。セマフォを解放するには、スレッドは Release メソッドの 1 つを呼び出す必要があります。カウントが 0 になると、他のスレッドがセマフォを解放するまで、Wait メソッドのいずれかへの後続の呼び出しがブロックされます。複数のスレッドがブロックされている場合、FIFO や LIFO など、スレッドがセマフォに入るタイミングを制御する保証された順序はありません。

C# の SemaphoreSlim クラスを理解する例:

以下の例では、リソースへのアクセスを提供する SemaphoreSlimFunction という関数を作成し、Wait メソッドはリソースにアクセスできるようになるまで現在のスレッドをブロックし、Release メソッドは作業が完了したら、リソースを解放する必要があります。 SemaphoreSlim を理解するために、Main メソッド内に SemaphoreSlimFunction に同時にアクセスしようとする 5 つのスレッドを作成しましたが、SemaphoreSlim オブジェクトを使用してアクセスを 3 つに制限しました。

using System;
using System.Threading;

namespace SemaphoreSlimDemo
{
    class Program
    {
        //only 3 threads can access resource simulteniously
        static SemaphoreSlim semaphore = new SemaphoreSlim(initialCount:3);

        static void Main(string[] args)
        {
            for (int i = 1; i <= 5; i++)
            {
                int count = i;
                Thread t = new Thread(() => SemaphoreSlimFunction("Thread " + count, 1000 * count));
                t.Start();
            }
            Console.ReadLine();
        }

        static void SemaphoreSlimFunction(string name, int seconds)
        {
            Console.WriteLine($"{name} Waits to access resource");
            semaphore.Wait();
            Console.WriteLine($"{name} was granted access to resource");

            Thread.Sleep(seconds);
            Console.WriteLine($"{name} is completed");
            semaphore.Release();
        }
    }
}
出力:

注: SemaphoreSlim コンストラクトを使用して、リソースにアクセスできる同時スレッドを制限します。リソースにアクセスしようとしているスレッドが宣言された制限を超えている場合、制限されたスレッドのみがアクセスを許可され、他のスレッドは待機する必要があります。

C# の SemaphoreSlim クラスを理解するための別の例:

以下の例では、最大数 3 スレッド、初期カウント 0 スレッドの SemaphoreSlim インスタンスを 1 つ作成します。次に、この例では 5 つのタスクを開始します。これらのタスクはすべて、セマフォを待機しているブロックです。メイン スレッドは、Release(Int32) オーバーロードを呼び出してセマフォ カウントを最大まで増やします。これにより、3 つのタスクがセマフォに入ることができます。セマフォが解放されるたびに、以前のセマフォ数が表示されます。

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
    // Create the semaphore.
    private static SemaphoreSlim semaphore = new SemaphoreSlim(0, 3);

    // A padding interval to make the output more orderly.
    private static int padding;

    public static void Main()
    {
        Console.WriteLine($"{semaphore.CurrentCount} tasks can enter the semaphore");
        Task[] tasks = new Task[5];

        // Create and start five numbered tasks.
        for (int i = 0; i <= 4; i++)
        {
            tasks[i] = Task.Run(() =>
            {
                // Each task begins by requesting the semaphore.
                Console.WriteLine($"Task {Task.CurrentId} begins and waits for the semaphore");

                int semaphoreCount;
                semaphore.Wait();
                try
                {
                    Interlocked.Add(ref padding, 100);
                    Console.WriteLine($"Task {Task.CurrentId} enters the semaphore");
                    // The task just sleeps for 1+ seconds.
                    Thread.Sleep(1000 + padding);
                }
                finally
                {
                    semaphoreCount = semaphore.Release();
                }
                Console.WriteLine($"Task {Task.CurrentId} releases the semaphore; previous count: {semaphoreCount}");
            });
        }

        // Wait for one second, to allow all the tasks to start and block.
        Thread.Sleep(1000);

        // Restore the semaphore count to its maximum value.
        Console.Write("Main thread calls Release(3) --> ");
        semaphore.Release(3);
        Console.WriteLine($"{semaphore.CurrentCount} tasks can enter the semaphore");
        // Main thread waits for the tasks to complete.
        Task.WaitAll(tasks);

        Console.WriteLine("Main thread Exits");
        Console.ReadKey();
    }
}
出力:

次の記事では、C# のマルチスレッド アプリケーションでデッドロックが発生する理由と方法について説明します。 例で。ここで、この記事では、C# で SemaphoreSlim クラスを使用してスレッド同期を実装する方法について説明します。 例で。この記事を楽しんで、例を使用して C# の SemaphoreSlim クラスの概念を理解していただければ幸いです。