web-dev-qa-db-ja.com

ThreadStatic属性はどのように機能しますか?

どうやって [ThreadStatic]属性が機能しますか?コンパイラはTLSの値を詰め込む/取り出すためにILを出力すると想定していましたが、逆アセンブリを見ると、そのレベルでは実行されないようです。

フォローアップとして、非静的メンバーに配置するとどうなりますか?開発者にその間違いをさせて、コンパイラーは警告を出しませんでした。

更新

2番目の質問はここで答えました: ThreadStatic Modified with Static C#

131
joshperry

スレッドスタティックの実装セマンティクスは、.NET jitコンパイラーではILレベル以下です。 VB.NETやC#のようにILに発行するコンパイラーは、ThreadStatic属性を持つ変数を読み書きできるILコードを発行するためにWin32 TLSについて何も知る必要がありません。 C#が知る限り、この変数について特別なことは何もありません-これは、単に物を読み書きする場所です。それに属性があるという事実は、C#には何の影響もありません。 C#は、そのシンボル名に対してIL読み取りまたは書き込み命令を発行することのみを知っている必要があります。

「重いリフティング」は、特定のハードウェアアーキテクチャでILを機能させるコアCLRによって実行されます。

また、不適切な(非静的)シンボルに属性を設定してもコンパイラから反応が得られない理由も説明できます。コンパイラーは、属性に必要な特別なセマンティクスを知りません。ただし、FX/Copなどのコード分析ツールはそれを知っている必要があります。

別の見方:CILは一連のストレージスコープを定義します:静的(グローバル)ストレージ、メンバーストレージ、およびスタックストレージ。 TLSがそのリストに載っていないのは、TLSがそのリストに載っている必要がないためです。シンボルにTLS属性がタグ付けされているときに、ILの読み取りおよび書き込み命令でTLSにアクセスするのに十分な場合、ILにTLSの特別な表現または処理が必要なのはなぜですか?必要ありません。

87
dthorpe

[ThreadStatic]属性はどのように機能しますか?

ThreadStatic でマークされたフィールドはスレッドに関連付けられており、その有効期間はスレッドの有効期間に匹敵すると考えることができます。

そのため、擬似コードではThreadStaticは(意味論により)スレッドにキー値を付加することに似ています。

Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myvariable"] += 1;

しかし、構文は少し簡単です:

class MyClass {
  [ThreadStatic]
  static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;

非静的メンバーに配置するとどうなりますか?

私はそれが無視されると信じています:

    class A {
        [ThreadStatic]
        public int a;
    }
    [Test]
    public void Try() {
        var a1 = new A();
        var a2 = new A();
        a1.a = 5;
        a2.a = 10;
        a1.a.Should().Be.EqualTo(5);
        a2.a.Should().Be.EqualTo(10);
    }

さらに、ThreadStaticは通常の静的フィールドと比較して同期メカニズムを必要としないことに注意する必要があります(状態が共有されないため)。

109

[ThreadStatic]は、各スレッドで同じ変数の分離バージョンを作成します。

例:

[ThreadStatic] public static int i; // Declaration of the variable i with ThreadStatic Attribute.

public static void Main()
{
    new Thread(() =>
    {
        for (int x = 0; x < 10; x++)
        {
            i++;
            Console.WriteLine("Thread A: {0}", i); // Uses one instance of the i variable.
        }
    }).Start();

    new Thread(() =>
   {
       for (int x = 0; x < 10; x++)
       {
           i++;
           Console.WriteLine("Thread B: {0}", i); // Uses another instance of the i variable.
       }
   }).Start();
}
5
Rui Ruivo