スレッド局所記憶
スレッド局所記憶(英: Thread local storage, TLS)は、静的もしくは大域的なメモリをスレッドごとに局所的に使用するためのコンピュータプログラミングの方法である。
プロセス内のスレッドはすべてアドレス空間を共有しており、同じプロセスのスレッドから参照する際、静的変数やグローバル変数は同じメモリ番地に配置される。
一方スタック上の変数はすべてのスレッドが自分のスタックを持つためスレッドに対して局所的であり、異なるメモリ番地に存在する。
同じ静的変数・グローバル変数を参照する二つのスレッドが(変数をスレッドに対して局所的にすることで)実際には異なるメモリ番地を参照できることが望ましい場合がある。たとえば典型的な例として C のエラーコードを格納する変数 errno がある。
少なくともメモリアドレスを格納できるサイズの変数をスレッドに対して局所的にすることが可能なら、メモリブロックを確保し、そのメモリアドレスをスレッドローカルな変数に格納すれば、原理的には任意のサイズのメモリブロックをスレッド局所にすることが可能である。
目次 |
Windows での実装 [編集]
API 関数 TlsAlloc を用いると、未使用の TLS スロット番号を取得できる。これによりその番号は使用中となる。
次にTlsGetValue および TlsSetValue 関数を用いて、番号によって識別されるスレッドローカルな変数に対する読み書きができる。TlsSetValue は現在のスレッドの変数にしか影響を与えない。
TlsFree 関数を用いると、TLS スロット番号 を解放することができる。番号は再び未使用となり、あらたな TlsAlloc により再度使用される。
Pthreads での実装 [編集]
POSIXスレッドでの TLS (PThread での名称は スレッド固有データ )は、Windows における TlsAlloc などの関数や機能と同様である。pthread_key_create で (オプションでdestructor を与えることも可能な) keyを生成し、このキーはpthread_setspecific によってデータと関連付けることができる。データは pthread_getspecific を用いて取得することができる。スレッド固有データが NULL でなければ、スレッドが終了する際 destructor が呼ばれる。また、key は pthread_key_delete で破棄しなければならない。
プログラミング言語固有の実装 [編集]
プログラマが適切な API 関数を呼ぶのではなく、プログラミング言語自体が TLS をサポートするよう拡張することもできる。
Object Pascal [編集]
Delphi や Free Pascal では、'var' ではなく予約語 'threadvar' を用いてスレッド局所記憶を用いることができる。
var mydata_process: integer; threadvar mydata_threadlocal: integer;
Java [編集]
Java ではスレッドローカルの変数は、クラスによって実現されている。ThreadLocal のオブジェクトは、オブジェクトの get や set をコールする各スレッドごとに変数の異なるインスタンスを保持する。以下は、(J2SE 5.0 以降) Integer オブジェクトを保持するThreadLocal を用いた例である:
ThreadLocal<Integer> local = new ThreadLocal<Integer>(){ @Override protected Integer initialValue(){ return 1; } };
はじめのコードで ThreadLocal オブジェクト local を 宣言、インスタンス化し、呼び出すスレッドから他の値が格納されなければ初期値 1 を返す。以下のコードは、現在の実行スレッドから値を 1 増やす。
local.set( local.get()+1 );
local.get() は、現在のスレッドに関連付けられた Integer オブジェクトを返す。コードでは local.set() を呼び出してスレッドに関連付けた値を設定する (上記の例は J2SE 5.0 で追加された generics と ボックス化を使用している)。
C/C++ [編集]
C言語では最新の C99 でもスレッドに関する規定が無く、処理系が独自に TLS を実装している。 次期規格 C1X ではキーワード _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<int> number;
2013年5月時点でバージョン4.8以降のGCCとバージョン3.3以降のClang(g++のランタイム使用時に限る)だけが動的初期化をサポートする。
C++11 より前から以下の処理系では独自に TLS を実装している。ただし以下全てのケースで動的初期化はサポートされない。
- Sun Studio C/C++, IBM XL C/C++, GNU C, Clang, Intel C/C++(Linux)
キーワード __thread を下記のように使用する:
__thread int number;
- Visual C++, Intel C/C++(Windows systems), Borland C++ Builder, Digital Mars C++
キーワード declspec(thread) を下記のように使用する:
__declspec(thread) int number;
- Borland C++ Builder ではキーワード __thread もサポートされる。
int __thread number;
Windows Vista および Server 2008 より前の Windows では、__declspec(thread) は実行ファイルにリンクされた DLL でのみ動作し、LoadLibrary() でロードされる DLL では動作しない(APIは成功を返すが TLS に関する処理が行われないため保護違反もしくはデータ破壊が発生する)。 TLS の使用にはこれ以外にもルールが存在する。MSDN の "Rules and Limitations for TLS" 参照。
C# および .NET 言語 [編集]
静的フィールドは、ThreadStaticAttributeでマークすることができる:
class FooBar
{
[ThreadStatic] static int foo;
}
また、API によって動的にスレッド局所変数を割り当てることもできる。
Python [編集]
Python バージョン 2.4 以降では、threading モジュールの local クラスを使ってスレッド局所記憶を作成することができる。
import threading mydata = threading.local() mydata.x = 1
Ruby [編集]
Ruby では、スレッド局所変数は []=および[] メソッドを使ってアクセスすることができる。
Thread.current[:user_id] = 1
Perl [編集]
Perl のスレッドは言語の進化に伴って CPAN で大半のコードが利用可能になった後で追加された。
その結果、 Perl では既存の非スレッドサポートのコードがスレッドのサポートにより影響を受けにくいようすべての変数をスレッドローカルとして扱うことになった。逆に Perl では、スレッドで共有される変数は attribte を用いて作成することができる。
use threads; use threads::shared; my $localvar; my $sharedvar :shared;
D言語 [編集]
D言語 の D2 では、グローバル変数および静的変数がデフォルトでスレッド局所変数になった。
この場合、スレッド間で共有される変数は shared修飾子もしくは __gshared属性 を用いて作成することができる。
module sample; int globalVar; // グローバル変数(スレッド局所記憶) shared(int) globalSharedVar; // (スレッド間共有)