技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

C#:セマフォを用いた排他制御

排他制御の仕組みとして先日Mutexを取り上げました(https://akira2kun.hatenablog.com/entry/2020/04/12/170559)。
今回は、同じく排他制御で使われるセマフォについて取り上げます。
 
セマフォがMutexと異なる点は、複数のプロセス・スレッドが資源を取得することができることです。
セマフォのコンストラクタで初期で解放する資源数や、解放できる資源の最大数を指定します。
WaitOne関数で資源取得待ちを行い、Release関数で資源解放を行います。Release関数の引数で資源解放数を指定することもできます。
(ただし、最大数を超える資源を開放するとSemaphoreFullExceptionとなるので注意が必要です)
 
セマフォは、同時に動くスレッド数を制限したいような重い処理がある時によく使われます。
 
以下は、セマフォを用いて同時に動くスレッド数を制限するサンプルです。
 
【サンプルコード】
・Controller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Controller
{
    internal class Controller
    {
        public static void Main(string args)
        {
            using(Semaphore sem = new Semaphore(0, 2, "semaphore"))
            {
                Console.WriteLine(DateTime.Now +
                    ":セマフォを作りました。" +
                    "初期で解放0個、最大で2つまで解放します。");
                Thread.Sleep(2000);
                sem.Release(2);
                Console.WriteLine(DateTime.Now +
                    ":準備ができたので、セマフォを2つ解放しました。");
                Thread.Sleep(10000);
            }
            Console.ReadLine();
        }
    }
}
 
・Processor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Processor
{
    internal class Processor
    {
        public static void Main(string
args)
        {
            const int N = 3;
            Parallel.For(0, N, id => // id0~2のスレッドを生成
            {
                Semaphore sem;
                if (Semaphore.TryOpenExisting("semaphore", out sem))
                {
                    Console.WriteLine("ID" + id + ":" + DateTime.Now +
                        ":セマフォ解放待ち。");
                    sem.WaitOne();
                    Console.WriteLine("ID" + id + ":" + DateTime.Now +
                        ":セマフォ取得しました。処理開始します。");
                    Thread.Sleep(2000);
                    Console.WriteLine("ID" + id + ":" + DateTime.Now +
                        ":処理終了しました。");
                    sem.Release();
                    Console.WriteLine("ID" + id + ":" + DateTime.Now +
                        ":処理終了したので、セマフォを1つ解放しました。");
                }
                else
                {
                    Console.WriteLine("ID" + id + ":" + DateTime.Now +
                        ":セマフォが見つかりません。");
                }
            });
            Console.ReadLine();
        }
    }
}
 
【実行用バッチ】
・test.bat
@echo off
echo 最大で2つのスレッドしか動かないようにするサンプルです。
echo 同時に動くスレッド数を制限したい重い処理がある時に使えます。
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Controller.cs
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Processor.cs
start Controller.exe
timeout 1
start Processor.exe
pause
exit
 
【実行結果】
・test.bat
最大で2つのスレッドしか動かないようにするサンプルです。
同時に動くスレッド数を制限したい重い処理がある時に使えます。
Microsoft (R) Visual C# Compiler version 4.7.3062.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, w
hich is no longer the latest version. For compilers that support newer versions of the C# programming language, see http
://go.microsoft.com/fwlink/?LinkID=533240

Microsoft (R) Visual C# Compiler version 4.7.3062.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, w
hich is no longer the latest version. For compilers that support newer versions of the C# programming language, see http
://go.microsoft.com/fwlink/?LinkID=533240


0 秒待っています。続行するには何かキーを押してください ...
続行するには何かキーを押してください . . .
 
・Controller.exe
2020/05/09 13:48:45:セマフォを作りました。初期で解放0個、最大で2つまで解放します。
2020/05/09 13:48:47:準備ができたので、セマフォを2つ解放しました。
 
・Processor.exe
ID0:2020/05/09 13:48:46:セマフォ解放待ち。
ID2:2020/05/09 13:48:46:セマフォ解放待ち。
ID1:2020/05/09 13:48:46:セマフォ解放待ち。
ID0:2020/05/09 13:48:47:セマフォ取得しました。処理開始します。
ID2:2020/05/09 13:48:47:セマフォ取得しました。処理開始します。
ID2:2020/05/09 13:48:49:処理終了しました。
ID0:2020/05/09 13:48:49:処理終了しました。
ID0:2020/05/09 13:48:49:処理終了したので、セマフォを1つ解放しました。
ID2:2020/05/09 13:48:49:処理終了したので、セマフォを1つ解放しました。
ID1:2020/05/09 13:48:49:セマフォ取得しました。処理開始します。
ID1:2020/05/09 13:48:51:処理終了しました。
ID1:2020/05/09 13:48:51:処理終了したので、セマフォを1つ解放しました。