いろいろ備忘録日記

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

.NET クラスライブラリ探訪-061 (System.Threading.ThreadLocal, TLS, スレッドローカルストレージ, ThreadStatic, .NET4.0から追加)


今回は、System.Threading.ThreadLocalクラスについてちょこっとメモメモ。


ThreadLocalクラスは.NET Framework 4.0から追加された型です。
ThreadStatic属性と同じく、スレッドローカルストレージ(TLS)を定義するために利用します。


以前のバージョンから存在するThreadStatic属性については、以下の記事を参照ください。


従来より存在していたThreadStatic属性は、以下の点が行えませんでした。

  • インスタンスフィールドには対応していない。(staticフィールドのみ) (インスタンスフィールドにも属性を付与することが出来るが、ちゃんと動作しない)
  • フィールドの値は常に、その型のデフォルト値で初期化される。初期値を設定しても無視される。


ThreadLocalは、上記の点を解決しています。
つまり

  • インスタンスフィールドに対応している。
  • フィールドの値を初期値で初期化出来る。


利用方法は、System.Lazyと似ており、コンストラクタに初期化のためのデリゲートを渡します。

ThreadLocal<int> _id = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId);

valueFactoryに指定した値は遅延評価され、それぞれのスレッドの最初のアクセス時に評価されます。



以下サンプルです。

  #region ThreadLocalSamples-01
  /// <summary>
  /// ThreadLocal<T>クラスのサンプルです。
  /// </summary>
  public class ThreadLocalSamples01 : IExecutable
  {
    #region Static Fields
    // ThreadStatic
    [ThreadStatic]
    static int count = 2;
    static ThreadLocal<int> count2 = new ThreadLocal<int>(() => 2);
    #endregion
    
    #region Fields
    [ThreadStatic]
    int count3 = 2;
    ThreadLocal<int> count4 = new ThreadLocal<int>(() => 4);
    #endregion
    
    public void Execute()
    {
      //
      // ThreadLocal<T>は、.NET 4.0から追加された型である。
      // ThreadStatic属性と同様に、スレッドローカルストレージ(TLS)を表現するための型である。
      //
      // 従来より存在していたThreadStatic属性には、以下の点が行えなかった。
      //   ・インスタンスフィールドには対応していない。(staticフィールドのみ)
      //    (インスタンスフィールドにも属性を付与することが出来るが、ちゃんと動作しない)
      //   ・フィールドの値は常に、その型のデフォルト値で初期化される。初期値を設定しても無視される。
      //
      // ThreadLocal<T>は、上記の点を解決している。つまり
      //   ・インスタンスフィールドに対応している。
      //   ・フィールドの値を初期値で初期化出来る。
      //
      // 利用方法は、System.Lazyと似ており、コンストラクタに初期化のためのデリゲートを渡す。
      //
      
      //
      // staticフィールドのThreadState属性の確認
      // ThreadStatic属性では、初期値を宣言時に設定していても無視され、強制的にデフォルト値が適用されるので
      // 出力される値は、全て0となる。
      //
      int numberOfParallels = 10;
      using (var countdown = new CountdownEvent(numberOfParallels))
      {
        for (var i = 0; i < numberOfParallels; i++)
        {
          int tmp = i;
          new Thread(() => { Console.WriteLine("ThreadStatic [count]>>> {0}", count++); countdown.Signal(); }).Start();
        }
        
        countdown.Wait();
      }
      
      //
      // staticフィールドのThreadLocal<T>の確認
      // ThreadLocal<T>は、初期値を設定できるので、出力される値は2となる。
      //
      using (var countdown = new CountdownEvent(numberOfParallels))
      {
        for (var i = 0; i < numberOfParallels; i++)
        {
          new Thread(() => { Console.WriteLine("ThreadLocal<T> [count2]>>> {0}", count2.Value++); countdown.Signal(); }).Start();
        }
        
        countdown.Wait();
      }
      
      //
      // インスタンスフィールドのThreadStatic属性の確認
      // ThreadStatic属性は、インスタンスフィールドに対しては効果が無い。
      // なので、出力される値は2,3,4,5,6...とインクリメントされていく.
      //
      using (var countdown = new CountdownEvent(numberOfParallels))
      {
        for (var i = 0; i < numberOfParallels; i++)
        {
          int tmp = i;
          new Thread(() => { Console.WriteLine("ThreadStatic [count3]>>> {0}", count3++); countdown.Signal(); }).Start();
        }
        
        countdown.Wait();
      }
      
      //
      // インスタンスフィールドのThreadLocal<T>の確認
      // ThreadLocal<T>は、インスタンスフィールドに対しても問題なく利用できる。
      // なので、出力される値は4となる。
      //
      using (var countdown = new CountdownEvent(numberOfParallels))
      {
        for (var i = 0; i < numberOfParallels; i++)
        {
          new Thread(() => { Console.WriteLine("ThreadLocal<T> [count4]>>> {0}", count4.Value++); countdown.Signal(); }).Start();
        }
        
        countdown.Wait();
      }
      
      count2.Dispose();
      count4.Dispose();
    }
  }
  #endregion


以下、実行結果です。

  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadStatic [count]>>> 0
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadLocal [count2]>>> 2
  ThreadStatic [count3]>>> 2
  ThreadStatic [count3]>>> 3
  ThreadStatic [count3]>>> 4
  ThreadStatic [count3]>>> 5
  ThreadStatic [count3]>>> 6
  ThreadStatic [count3]>>> 7
  ThreadStatic [count3]>>> 8
  ThreadStatic [count3]>>> 9
  ThreadStatic [count3]>>> 10
  ThreadStatic [count3]>>> 11
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4
  ThreadLocal [count4]>>> 4

ThreadLocalがどちらのパターンでも適切に動作しているのがわかります。


以下、参考資料です。

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

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