いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-021 (System.Threading.Thread (2))(スレッドローカルストレージ, TLS, GetData, SetData, LocalDataStoreSlot)


引き続き、Threadクラスさんです。
今回は、データスロットについて。


前回、スレッドにてデータを持つためには状態クラスを利用する方法があると
記述しました。それとは別にスレッドにはスレッドローカルストレージという機能があります。
TLSといいます。


ざっくりというと、各スレッドに固有のデータを持てるデータの枠(スロット)を作成することとなります。
スロットには、名前が付いていない無名スロットと名前がついているスロットの2つがあります。
また、このデータスロットは、スレッドとアプリケーションドメインの組み合わせで一意となります。


で、そのスロットを利用する際に使うのが以下のメソッドです。

  • AllocateDataSlot
  • GetData
  • SetData
  • GetNamedDataSlot
  • FreeNamedDataSlot


後、実際にスロットを表すクラスとして

  • LocalDataStoreSlot

があります。


以下、サンプルです。

#region ThreadingNamespaceSamples-02
    public class ThreadingNamespaceSamples02 : IExecutable{

        public void Execute(){
            ////////////////////////////////////////////////////////////
            //
            // 無名データスロット.
            //      データスロットはどれかのスレッドで最初に作成したら
            //      全てのスレッドに対してスロットが割り当てられる。
            //
            LocalDataStoreSlot slot = Thread.AllocateDataSlot();

            List<Thread> threads = new List<Thread>();
            for(int i = 0; i < 10; i++){
                Thread thread       = new Thread(DoAnonymousDataSlotProcess);
                thread.Name         = string.Format("Thread-{0}", i);
                thread.IsBackground = true;

                threads.Add(thread);

                thread.Start(slot);
            }

            threads.ForEach(thread => { thread.Join(); });

            Console.WriteLine(string.Empty);

            ////////////////////////////////////////////////////////////
            //
            // 名前付きデータスロット.
            //      名前がつけられる事以外は、無名のスロットと同じです。
            //      名前付きデータスロットは、最初にその名前が呼ばれた
            //      際に作成されます。
            //      FreeNamedDataSlotメソッドを呼ぶと現在のスロット設定
            //      が解放されます。
            //
            threads.Clear();
            for(int i = 0; i < 10; i++){
                Thread thread       = new Thread(DoNamedDataSlotProcess);
                thread.Name         = string.Format("Thread-{0}", i);
                thread.IsBackground = true;

                threads.Add(thread);

                thread.Start(slot);
            }

            threads.ForEach(thread => { thread.Join(); });

            //
            // 利用したデータスロットを解放.
            //
            Thread.FreeNamedDataSlot("SampleSlot");

        }

        void DoAnonymousDataSlotProcess(object stateObj){
            LocalDataStoreSlot slot = stateObj as LocalDataStoreSlot;

            //
            // スロットにデータを設定
            //
            Thread.SetData(slot, string.Format("ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId));

            //
            // 設定した内容を確認.
            //
            Console.WriteLine("[BEFORE] Thread:{0}   DataSlot:{1}", Thread.CurrentThread.Name, Thread.GetData(slot));

            //
            // 別のスレッドに処理を行って貰う為に一旦Sleepする。
            //
            Thread.Sleep(TimeSpan.FromSeconds(1));

            //
            // 再度確認.
            //
            Console.WriteLine("[AFTER ] Thread:{0}   DataSlot:{1}", Thread.CurrentThread.Name, Thread.GetData(slot));
        }

        void DoNamedDataSlotProcess(object stateObj){
            //
            // スロットにデータを設定
            //
            Thread.SetData(Thread.GetNamedDataSlot("SampleSlot"), string.Format("ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId));

            //
            // 設定した内容を確認.
            //
            Console.WriteLine("[BEFORE] Thread:{0}   DataSlot:{1}", Thread.CurrentThread.Name, Thread.GetData(Thread.GetNamedDataSlot("SampleSlot")));

            //
            // 別のスレッドに処理を行って貰う為に一旦Sleepする。
            //
            Thread.Sleep(TimeSpan.FromSeconds(1));

            //
            // 再度確認.
            //
            Console.WriteLine("[AFTER ] Thread:{0}   DataSlot:{1}", Thread.CurrentThread.Name, Thread.GetData(Thread.GetNamedDataSlot("SampleSlot")));
        }
    }
#endregion


とまあ、上でいろいろデータスロットについて書いてましたが
実は、MSDN内でもGetData/SetDataを用いた方法はあまり推奨されていません。


現実的には、GetData/SetDataを利用するよりも以下の属性を用いて
各スレッド毎の固有データを保持したりします。

ThreadStaticAttribute

MSDNによると、ThreadStaticAttributeを利用するほうがパフォーマンスや
コンパイラによる型チェックなどがあるなど恩恵があると記載されています。


てことで、次はThreadStaticAttributeについて書きたいと思います。



================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。