ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

スレッドセーフなカウンタ(Interlockedクラス)

プログラミングC# p515

排他制御の典型的なやつ。入金と出金。
残高が100万円ある口座に対して、20万円の引き出しと30万円の預け入れを同時にやってみる。

using System;
using System.Threading;

class ThreadTest
{
        private static int zandaka = 1000000;

        static void Main(string[] args)
        {
                ThreadStart ts1 = new ThreadStart(delegate(){
                        int currentZandaka = zandaka;

                        // 残高を使ったチェックなどの処理
                        Thread.Sleep(100);

                        // 残高を減額(引き出し)
                        zandaka = currentZandaka - 200000;
                        //Interlocked.Add(ref zandaka, -200000);
                });
                ThreadStart ts2 = new ThreadStart(delegate(){
                        int currentZandaka = zandaka;

                        // 残高を使ったチェックなどの処理
                        Thread.Sleep(100);

                        // 残高を増額(預け入れ)
                        zandaka = currentZandaka + 300000;
                        //Interlocked.Add(ref zandaka, 300000);
                });

                Thread t1 = new Thread(ts1);
                Thread t2 = new Thread(ts2);

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

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

                Console.WriteLine("zandaka = {0}", zandaka);
        }
}

結果はこう。(実行環境によっては違う場合もあり)

zandaka = 1300000

100万 - 20万 + 30万 = 130万円(?) というおいしい結果に(汗
というわけでコメントを付けはずしして、Interlockオブジェクトを使うようにする。

using System;
using System.Threading;

class ThreadTest
{
        private static int zandaka = 1000000;

        static void Main(string[] args)
        {
                ThreadStart ts1 = new ThreadStart(delegate(){
                        int currentZandaka = zandaka;

                        // 残高を使ったチェックなどの処理
                        Thread.Sleep(100);

                        // 残高を減額(引き出し)
                        //zandaka = currentZandaka - 200000;
                        Interlocked.Add(ref zandaka, -200000);
                });
                ThreadStart ts2 = new ThreadStart(delegate(){
                        int currentZandaka = zandaka;

                        // 残高を使ったチェックなどの処理
                        Thread.Sleep(100);

                        // 残高を増額(預け入れ)
                        //zandaka = currentZandaka + 300000;
                        Interlocked.Add(ref zandaka, 300000);
                });

                Thread t1 = new Thread(ts1);
                Thread t2 = new Thread(ts2);

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

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

                Console.WriteLine("zandaka = {0}", zandaka);
        }
}

結果はこう。

zandaka = 1100000

正しく計算されました。