規則と制約 (TLS)

更新 : 2007 年 11 月

静的に連結されるスレッド ローカル オブジェクトや変数を宣言する場合は、次のガイドラインに従ってください。

  • thread 属性は、データの宣言と定義だけに使うことができます。関数の宣言や定義には使用できません。たとえば、次のコードはコンパイラ エラーになります。

    #define Thread  __declspec( thread )
    Thread void func();     // This will generate an error.
    
  • thread 修飾子は、静的なデータ項目に対してだけ指定できます。静的オブジェクトとは、グローバルなデータ オブジェクト (static と extern の両方) や、ローカルな静的オブジェクト、C++ クラスの静的データ メンバのいずれかです。auto データ オブジェクトの宣言には、thread 属性を使用できません。次のコードはコンパイル エラーになります。

    #define Thread  __declspec( thread )
    void func1()
    {
        Thread int tls_i;            // This will generate an error.
    }
    
    int func2( Thread int tls_i )    // This will generate an error.
    {
        return tls_i;
    }
    
  • スレッドのローカル オブジェクトの宣言や定義には、thread 属性を指定する必要があります。たとえば、次のコードはエラーになります。

    #define Thread  __declspec( thread )
    extern int tls_i;        // This will generate an error, since the
    int Thread tls_i;        // declaration and definition differ.
    
  • thread 属性は、型修飾子として使うことはできません。たとえば、次のコードはコンパイラ エラーになります。

    char __declspec( thread ) *ch;        // Error
    
  • C++ クラスに対しては、thread 属性を使えません。ただし、C++ クラス オブジェクトは、thread 属性でインスタンス化できます。たとえば、次のコードはコンパイラ エラーになります。

    #define Thread  __declspec( thread )
    class Thread C       // Error: classes cannot be declared Thread.
    {
    // Code
    };
    C CObject;
    

    thread 属性を使用する C++ オブジェクトの宣言は許可されるので、次の 2 つの例の意味は同じになります。

    #define Thread  __declspec( thread )
    Thread class B
    {
    // Code
    } BObject;               // OK--BObject is declared thread local.
    
    class B
    {
    // Code
    };
    Thread B BObject;        // OK--BObject is declared thread local.
    
  • スレッドのローカル オブジェクトのアドレスは定数とは見なされず、またそのようなアドレスが含まれている式は、定数式とは見なされません。このため標準の C では、オブジェクトやポインタに対する初期化子として、スレッド ローカル変数のアドレスを使うことはできません。たとえば、C コンパイラは次のコードをエラーとします。

    #define Thread  __declspec( thread )
    Thread int tls_i;
    int *p = &tls_i;       //This will generate an error in C.
    

    ただし、このような制約は C++ にはありません。C++ ではすべてのオブジェクトを動的に初期化できるので、スレッドのローカル変数のアドレスを使っている式でオブジェクトを初期化できます。これは、スレッドのローカル オブジェクトの構築と同じように行われます。たとえば、上記のコードは、C++ ソース ファイルとしてコンパイルする場合はエラーになりません。ただし、スレッドのローカル変数のアドレスは、アドレスが取られたスレッドが存在している間だけ有効であることに注意する必要があります。

  • 標準 C では、自分自身を参照している式でオブジェクトや変数を初期化できます。ただし、この場合のオブジェクトは、非静的なものに限られます。C++ では通常、自分自身を参照している式でオブジェクトを動的に初期化できますが、このような初期化はスレッドのローカル オブジェクトではできません。以下にサンプルを示します。

    #define Thread  __declspec( thread )
    Thread int tls_i = tls_i;                // Error in C and C++ 
    int j = j;                               // OK in C++, error in C
    Thread int tls_i = sizeof( tls_i )       // Legal in C and C++
    

    初期化されるオブジェクトが含まれている sizeof 式は、式自体を参照しないので、C と C++ の両方で有効です。

    C++ では、スレッド ローカル ストレージの将来の機能拡張に備えるため、スレッド データをこのように動的に初期化できません。

  • DLL がローカルでないデータやオブジェクトを __declspec (thread) として宣言すると、動的に読み込まれた場合に保護違反となります。DLL が LoadLibrary により読み込まれた後で、コードがローカルでない __declspec (thread) データを参照するたびにシステム障害が発生します。スレッドのグローバル変数領域は実行時に割り当てられるので、この領域のサイズは、アプリケーションの必要量と、静的にリンクするすべての DLL の必要量の合計を計算することによって決まります。LoadLibrary を使った場合はこの領域を拡張できないので、__declspec (thread) で宣言したスレッドのローカル変数を入れる余地がありません。DLL を LoadLibrary で読み込む予定の場合は、DLL で TlsAlloc などの TLS API を使って TLS を割り当てるようにします。

参照

概念

スレッド ローカル ストレージ (TLS: Thread Local Storage)