Amazonでお得な買い物

【C#】排他制御はどういうときに使う?

C# 排他制御

今回はC#の排他制御について解説します。

目次
スポンサーリンク

排他制御とは?

排他制御は、マルチスレッドやマルチプロセス環境で共有リソースへのアクセスを管理するための重要な動作です。

排他制御の目的は、競合状態を防ぎ、データ整合性やプログラムの安全性を確保することです。

競合状態は、複数のスレッドやプロセスが同時に共有リソースにアクセスし、予測不能な結果やエラーが発生する可能性がある状態を指します。

排他制御を実装することで、競合状態を防ぎ、正確で安全な動作を確保できます。

排他制御のサンプルコード

次にサンプルコードを提示していきます。

排他制御を使わない場合

まずは、排他制御を使わなかった例です。

サンプルコード

using System;
using System.Threading;

class Program
{
    static int sharedCounter = 0;
    static int maxCounter = 100;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();

        Console.WriteLine("最終的なカウンターの値: " + sharedCounter);
        Console.WriteLine("本来想定していた値: " + maxCounter * 2);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < maxCounter; i++)
        {
            Thread.Sleep(50);
            sharedCounter++; // 共有変数への非同期アクセス
        }
    }
}

実行結果

最終的なカウンターの値: 196
本来想定していた値: 200

このコードでは、2つのスレッドが同時に sharedCounter 変数をインクリメントしようとしています。排他制御がないため、競合状態が発生し、最終的なカウンターの値は予測不能です。

毎回必ずズレてくれればいいですが、システムによっては10万回に1回しか発生しない現象であったりすることがあり、かなり厄介な問題になりやすいです。

具体的には、先ほどのコードのThread.Sleep(50);の処理を無くすと、最終的なカウンターが本来想定していた200回となり、10万回に1回だけ199回と出力されてしまうイメージです。

排他制御を使う場合

次に排他制御を使うとこのようになります。

サンプルコード

using System;
using System.Threading;

class Program
{
    static int sharedCounter = 0;
    static int maxCounter = 100;

    static object lockObject = new object();

    private static void Main(string[] args)
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();

        Console.WriteLine("最終的なカウンターの値: " + sharedCounter);
        Console.WriteLine("本来想定していた値: " + maxCounter * 2);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < maxCounter; i++)
        {
            lock (lockObject) // 排他制御用のロック
            {
                Thread.Sleep(50);
                sharedCounter++; // 共有変数への同期アクセス
            }
        }
    }
}

実行結果

最終的なカウンターの値: 200
本来想定していた値: 200

このコードでは、排他制御を実現するために lock キーワードを使用しています。lock ブロック内では、複数のスレッドが同時にアクセスできないようになります。したがって、競合状態を防ぎ、最終的なカウンターの値が正確になります。

排他制御を実装することで、複数のスレッドやプロセスがデータに安全にアクセスできるようになり、プログラムの信頼性と安定性が向上します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

都内の精密機器を作っている会社に勤務している14年目のエンジニアです。趣味は美味しいものを食べることとゴルフ。プログラムについて今まで学んだことをわかりやすく発信するサイトを目指しています。

コメント

コメントする

目次