C#でのMutexとSemaphoreの2セント

スレッド同期は、複数のスレッドが共有リソースに同時にアクセスするのを防ぐために使用されます。ミューテックスとセマフォは、最も重要な関連概念の2つです。これらの両方が何であるか、そしていつそれらを使用すべきかを理解しましょう。

議論を始める前に、基本的な概念を簡単に見てみましょう。スレッドは、プロセス内の実行の最小単位です。基本的に、マルチスレッドは複数のタスクを同時に実行するのに役立ち、アプリケーションの全体的なスループットを向上させます。

ミューテックスは、プロセス間で機能できる同期プリミティブです。つまり、プロセス間同期に使用できます。それどころか、セマフォは、同じ時点で共有リソースにアクセスできるスレッドの数を制限できるものです。本質的に、セマフォはミューテックスのより一般化された形式です。

セマフォは、共有リソースに同時にアクセスできるスレッドの数を制限するために使用されます。本質的には、特定の共有リソースのコンシューマーの数を同時に制限するために使用されます。セマフォを利用して、非排他的ロックを実装し、同時実行を制限することができます。

ミューテックスは、共有リソースの排他ロックに使用されることに注意してください。言い換えると、Mutexを使用すると、相互に排他的なロックを取得できます。つまり、任意の1つのスレッドが特定の時点で共有リソースにアクセスできます。排他的ロックは、任意の時点で、1つのスレッドだけがクリティカルセクションに入ることができるようにするために使用されます。クリティカルセクションは、複数のスレッドで共有されるデータ構造またはリソースとして定義できますが、任意の時点でアクセスできるのは1つのスレッドだけです。

System.Threading.Mutexクラスはミューテックスを表し、System.Threading.Semaphoreクラスはセマフォの操作に使用されます。MutexクラスのインスタンスでWaitOneメソッドを使用してロックし、ReleaseMutexメソッドを使用してロックを解除できます。

Mutex mutexObject = new Mutex(false, "Demo");

if (!mutexObject.WaitOne(TimeSpan.FromSeconds(10), false))

     {

             Console.WriteLine("Quitting for now as another instance is in execution...");

               return;

     }

C#でセマフォを作成するには、Semaphoreクラスのインスタンスを作成する必要があります。セマフォインスタンスを作成するときは、引数コンストラクターに2つの引数を渡す必要があります。最初の引数は初期リソースエントリの数を示すために使用されますが、2番目の引数は同時リソースエントリの最大数を指定するために使用されます。作成される新しいスレッド用にすべてのスロットを予約する場合は、これらのパラメーターの両方に同じ値を指定する必要があることに注意してください。次のコードスニペットは、C#でセマフォを作成する方法を示しています。

public static Semaphore threadPool = new Semaphore(3, 5);

上記のコードスニペットを参照してください。上記のステートメントは、最大5つの同時リクエストをサポートできるthreadPoolという名前のセマフォオブジェクトを作成します。コンストラクターの最初のパラメーターに示されているように、初期カウントは3に設定されていることに注意してください。これは、現在のスレッド用に2つのスロットが予約されており、他のスレッド用に3つのスロットが使用可能であることを意味します。コードを書いてみましょう!

次のコードスニペットは、System.Threading名前空間で使用可能なThreadクラスを使用して10個のスレッドを作成および開始する方法を示しています。ThreadStartデリゲートがどのように使用されているかに注意してください。

for (int i = 0; i < 10; i++)

{

   Thread threadObject = new Thread(new ThreadStart(PerformSomeWork));

   threadObject.Name = "Thread Name: " + i;

   threadObject.Start();

}

これがPerformSomeWorkメソッドのコードです。これは、セマフォを操作するためのコードを実際に含むメソッドです。

private static void PerformSomeWork()

       {

           threadPool.WaitOne();

           Console.WriteLine("Thread {0} is inside the critical section...", Thread.CurrentThread.Name);

           Thread.Sleep(10000);

           threadPool.Release();

       }

上記のPerformSomeWorkメソッドを参照してください。シグナルが受信されるまで現在のスレッドをブロックするために、SemaphoreインスタンスでWaitOneメソッドが呼び出されます。セマフォを解放するために、同じインスタンスでReleaseメソッドが呼び出されます。参考までに、完全なコードリストを次に示します。

class SemaphoreDemo

   {

       public static Semaphore threadPool = new Semaphore(3, 5);

       public static void Main(string[] args)

       {

           for (int i = 0; i < 10; i++)

           {

               Thread threadObject = new Thread(new ThreadStart(PerformSomeWork));

               threadObject.Name = "Thread Name: " + i;

               threadObject.Start();

           }

           Console.ReadLine();

       }

       private static void PerformSomeWork()

       {

           threadPool.WaitOne();

           Console.WriteLine("Thread {0} is inside the critical section...", Thread.CurrentThread.Name);

           Thread.Sleep(10000);

           threadPool.Release();

       }

   }