スレッド局所記憶

スレッド局所記憶は、静的もしくは大域的なメモリをスレッドごとに局所的に使用するためのコンピュータプログラミングの方法である。

プロセス内のスレッドはすべてアドレス空間を共有しており、同じプロセスのスレッドから参照する際、静的変数やグローバル変数は同じメモリ番地に配置される。

一方スタック上の変数はすべてのスレッドが自分のスタックを持つためスレッドに対して局所的であり、異なるメモリ番地に存在する。

同じ静的変数・グローバル変数を参照する二つのスレッドが実際には異なるメモリ番地を参照できることが望ましい場合がある。たとえば典型的な例としてC言語のエラーコードを格納する変数 errno がある。

少なくともメモリアドレスを格納できるサイズの変数をスレッドに対して局所的にすることが可能なら、メモリブロックを確保し、そのメモリアドレスをスレッドローカルな変数に格納すれば、原理的には任意のサイズのメモリブロックをスレッド局所にすることが可能である。

1. プラットフォーム固有実装
---------------

マルチスレッドをサポートするプラットフォームや規格には、通例TLSを利用するためのAPIが用意されている。

1.1. Windowsでの実装
----------------

Microsoft Windowsでは、Windows APIの関数 TlsAlloc を用いると、未使用のTLSスロット番号を取得できる。これによりその番号は使用中となる。

次にTlsGetValueおよびTlsSetValue関数を用いて、番号によって識別されるスレッドローカルな変数に対する読み書きができる。TlsSetValue は現在のスレッドの変数にしか影響を与えない。

TlsFree 関数を用いると、TLSスロット番号を解放することができる。番号は再び未使用となり、あらたな TlsAlloc により再度使用することが可能となる。

1.2. Pthreadsでの実装
-----------------

POSIXスレッド  ではスレッド固有データ  と呼ばれる。このスレッド固有データは、WindowsにおけるTLSの関数や機能と同様である。pthread_key_create で //key// を生成し、この //key// はpthread_setspecificによって任意のデータと関連付けることができる。各 //key// に関連付けられたデータは pthread_getspecific を用いて取得することができる。pthread_key_createはオプションで任意の //destructor// 関数を与えることも可能であり、スレッド固有データが NULL でなければ、スレッドが終了する際 //destructor// が呼ばれる。また、//key// は pthread_key_delete で破棄しなければならない。

2. プログラミング言語固有の実装
-----------------

プログラマが適切な API 関数を呼ぶのではなく、プログラミング言語自体が TLS をサポートするよう拡張することもできる。

2.1. C/C++
----------

C言語では C11 からキーワード _Thread_local を用いて TLS を使用できる。

C++ では C++11 においてスレッドに関する規定とキーワード thread_local が導入された。以下のケース全てで TLS を使用できる。

名前空間スコープ変数
ファイルスコープ静的変数
関数スコープ静的変数
静的メンバ変数thread_local を下記のように使用する:thread_local int number; また非定数初期化子の使用やコンストラクタ・デストラクタを呼び出すクラスも使用できる。 thread_local int number = some_non_constexpr_function; thread_local std::vector number;

C11/C++11 より前から以下の処理系では独自に TLS を実装している。

Sun Studio C/C++, IBM XL C/C++, GNU C, Clang, Intel C/C++ キーワード __thread を下記のように使用する:__thread int number;
Microsoft Visual C++, Intel C/C++ , C++ Builder, Digital Mars C++キーワード __declspec を下記のように使用する:__declspec int number;
C++ Builder ではキーワード __thread もサポートされる。int __thread number;Windows Vista および Windows Server 2008 より前の Windows では、__declspec は実行ファイルにリンクされた DLL でのみ動作し、LoadLibrary でロードされる DLL では動作しない 。 TLS の使用にはこれ以外にもルールが存在する。[1] の "Rules and limitations" を参照。

2.2. C# および .NET 言語
-------------------

静的フィールドは、[2]でマークすることができる: class FooBar { [ThreadStatic] static int foo; } また、[3]メソッドによって動的にスレッド局所変数を割り当てることもできる。

2.3. Java
---------

Java ではスレッドローカルの変数は、クラスname=java.lang.ThreadLocalによって実現されている。ThreadLocal のオブジェクトは、オブジェクトの get や set メソッドをコールする各スレッドごとに、変数の異なるインスタンスを保持する。以下は、Integer オブジェクトを保持するThreadLocal を用いた例である:

ThreadLocal local = new ThreadLocal { @Override protected Integer initialValue { return 1; } };

はじめのコードで ThreadLocal オブジェクト local を宣言、インスタンス化し、呼び出すスレッドから他の値が格納されなければ初期値 1 を返す。以下のコードは、現在の実行スレッドから値を 1 増やす。

local.set + 1);

local.getは、現在のスレッドに関連付けられた Integer オブジェクトを返す。また、local.set を呼び出してスレッドに関連付けた値を設定する。なお、上記の例は J2SE 5.0 で追加されたジェネリクスと自動ボックス化を使用している。

2.4. Pascal
-----------

Object Pascal  や Free Pascal では、var ではなくキーワード threadvar を用いてスレッド局所記憶変数を定義することができる。

var myData: Integer; threadvar myThreadLocalData: Integer;

2.5. Perl
---------

Perl のスレッドは言語の進化に伴って CPAN で大半のコードが利用可能になった後で追加された。

その結果、Perl では既存の非スレッドサポートのコードがスレッドのサポートにより影響を受けにくいようすべての変数をスレッドローカルとして扱うことになった。逆に Perl では、スレッドで共有される変数は attribute を用いて作成することができる。

use threads; use threads::shared;

my $localvar; my $sharedvar :shared;<WtXmlEndTag />

2.6. Python
-----------

Python バージョン 2.4 以降では、threading モジュールの local クラスを使ってスレッド局所記憶を作成することができる。

import threading mydata = threading.local mydata.x = 1

2.7. Ruby
---------

Ruby では、スレッド局所変数は []= および [] メソッドを使ってアクセスすることができる。

Thread.current[:user_id] = 1

2.8. D言語
--------

D言語 の D2 では、グローバル変数および静的変数がデフォルトでスレッド局所変数になった。

この場合、スレッド間で共有される変数は [4]作成することができる。

module sample;

int globalVar; // グローバル変数  shared globalSharedVar; // <WtXmlEndTag />

3. 外部リンク
--------

[5] — C/C++ での TLS の実装についての文書
[6]
[7]
"[8]" Doug Doedens による記事
"[9]" Lawrence Crowl による記事
"[10]