2003年12月22日
川俣晶の縁側ソフトウェア技術雑記 total 11842 count

C#のstaticコンストラクタが実行中に、他のスレッドから同じクラスのstaticメンバにアクセスがあったとき何が起こるか

Written By: 川俣 晶連絡先

 C#のstaticなコンストラクタは、staticなメンバやそのクラスのインスタンス作成に先んじて実行されます。これは、1プロセス上に1つしかない様々な資源の初期化に便利ではありますが。

 はたと不安になったことがありました。マルチスレッド環境だと何が起こるのでしょうか。たとえば、クラスCのstaticなメンバー変数Vにアクセスするスレッドが2つ走っていたとします。片方のスレッドがメンバー変数Vに最初にアクセスした時に、コンストラクタの実行が開始されますが、それが完了する前にもう1つのスレッドがメンバー変数Vにアクセスしたら何が起こるのでしょうか。

 C#言語仕様には特に何も書いていないので、おそらくstaticなコンストラクタの実行が完了されるまで全て待たされると思いましたが、100%の確信が持てません。

 というわけでテストプログラムを書いてみました。

using System;

using System.Threading;

namespace ConsoleApplication2

{

    public class TestClass

    {

        public static int A = 0;

        static TestClass()

        {

            Console.WriteLine("TestClassコンストラクタ called");

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

            Thread.Sleep( TimeSpan.FromSeconds(3) );

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

        }

    }

    class Class1

    {

        public static void ThreadProc1()

        {

            Console.WriteLine("ThreadProc1 called");

            TestClass.A ++;

            Console.WriteLine("A(3)={0}",TestClass.A);

        }

        public static void ThreadProc2()

        {

            Console.WriteLine("ThreadProc2 called");

            Thread.Sleep( TimeSpan.FromSeconds(1) );

            TestClass.A ++;

            Console.WriteLine("A(4)={0}",TestClass.A);

        }

        [STAThread]

        static void Main(string[] args)

        {

            Thread t1 = new Thread(new ThreadStart(ThreadProc1));

            Thread t2 = new Thread(new ThreadStart(ThreadProc2));

            t1.Start();

            t2.Start();

            t1.Join();

            t2.Join();

        }

    }

}

 このプログラムは、ThreadProc1が変数Aにアクセスしてstaticなコンストラクタを起動させ、その実行中にThreadProc2が変数Aの書き換えに行く、というものです。もし、正しく全てのアクセスがコンストラクタ実行完了まで待たされていれば、A(2)=0という結果を得るはずです。コンストラクタ実行中に変数を書き換えることができたなら、A(2)=1という結果になるはずです。

 実行結果は以下の通りになりました。(.NET Framework 1.1環境下)

ThreadProc1 called

TestClassコンストラクタ called

A(1)=0

ThreadProc2 called

A(2)=0

A(3)=1

A(4)=2

 というわけで、結果は好ましいものでした。

 マルチスレッド環境でも、staticなメンバーへのアクセスは、staticなコンストラクタの実行が完了されるまで遅延されているということですね。