スレッド局所記憶(TLS)とは
スレッド局所記憶(Thread Local Storage, TLS)とは、マルチスレッドプログラミングにおいて、各スレッドが独立したデータを保持するための技術です。通常、静的変数や
グローバル変数は全てのスレッドから共有されますが、TLSを用いることで、これらの変数をスレッドごとに局所的に扱うことができます。
なぜTLSが必要なのか
マルチスレッドアプリケーションでは、複数のスレッドが同時に同じメモリ領域にアクセスする可能性があります。このとき、共有される静的変数や
グローバル変数をそのまま使用すると、競合が発生し、予期せぬ動作を引き起こす可能性があります。TLSは、このような問題を回避し、各スレッドが安全にデータを利用できるようにするために導入されました。
例えば、
C言語のエラーコードを格納する `errno` 変数は、スレッドごとに異なるエラー情報を保持する必要があります。TLSを使用することで、各スレッドが自身の `errno` 変数を持つことができ、エラー情報の競合を防ぐことができます。
TLSの基本的な仕組み
TLSを実現するためには、各スレッドが専用のメモリ領域を持つ必要があります。このメモリ領域は、スレッドが作成される際に自動的に割り当てられ、スレッドが終了する際に解放されます。
原理的には、
メモリアドレスを格納できるサイズの変数をスレッドに対して局所的にすれば、メモリブロックを確保し、その
メモリアドレスをスレッドローカルな変数に格納することで、任意のサイズのメモリブロックをスレッド局所にすることができます。
各プラットフォーム・言語での実装
TLSは、様々なプラットフォームやプログラミング言語で実装されています。以下に代表的な例を挙げます。
Windows
Windowsでは、`TlsAlloc()`, `TlsGetValue()`, `TlsSetValue()`, `TlsFree()`といったAPI関数を用いてTLSを操作します。
- - `TlsAlloc()`: 未使用のTLSスロット番号を取得します。
- - `TlsSetValue()`: 現在のスレッドの指定されたスロットに値を設定します。
- - `TlsGetValue()`: 現在のスレッドの指定されたスロットから値を取得します。
- - `TlsFree()`: TLSスロット番号を解放します。
Pthreads
POSIXスレッド(Pthreads)では、スレッド固有データ(thread-specific data)と呼ばれる仕組みでTLSを実現します。
- - `pthread_key_create()`: スレッド固有のデータを識別するためのキーを生成します。
- - `pthread_setspecific()`: 指定されたキーに対応するデータを設定します。
- - `pthread_getspecific()`: 指定されたキーに対応するデータを取得します。
- - `pthread_key_delete()`: キーを破棄します。
C11以降では、`_Thread_local` キーワードを使ってTLS変数を宣言できます。
C++11では、`thread_local` キーワードが導入されました。
c
// C11
_Thread_local int my_var;
//
C++11
thread_local int my_var;
古い環境では、コンパイラ固有のキーワード(`__thread` や `__declspec(thread)`)を使用する必要があります。
C# (.NET)
C#では、`[ThreadStatic]` 属性を静的フィールドに付与することで、スレッドローカルな変数を作成できます。また、`Thread.GetNamedDataSlot(String)` メソッドを使って動的にスレッドローカル変数を割り当てることもできます。
csharp
[ThreadStatic]
static int my_var;
Javaでは、`java.lang.ThreadLocal` クラスを使用してTLSを実現します。`ThreadLocal` オブジェクトは、`get()` や `set()` メソッドを呼び出す各スレッドごとに、変数の異なるインスタンスを保持します。
java
ThreadLocal
local = ThreadLocal.withInitial(() -> 1);
int value = local.get();
local.set(value + 1);
Pascal
Object Pascal (Delphi) や Free Pascal では、`threadvar` キーワードを使ってスレッド局所変数を定義できます。
pascal
threadvar my_var: Integer;
Perlでは、デフォルトですべての変数がスレッドローカルとして扱われます。スレッド間で共有したい場合は、`attribute` を使用して共有変数を作成する必要があります。
Python 2.4以降では、`threading.local` クラスを使ってスレッド局所記憶を作成できます。
python
import threading
local_data = threading.local()
local_data.my_var = 10
Rubyでは、スレッド局所変数は `[]=` および `[]` メソッドを使ってアクセスできます。
ruby
Thread.current[:my_var] = 10
puts Thread.current[:my_var]
D言語のD2では、グローバル変数と静的変数がデフォルトでスレッド局所変数になりました。スレッド間で共有したい場合は、`shared` 修飾子または `__gshared` 属性を使用します。
TLSの注意点
TLSは便利な機能ですが、注意すべき点もあります。
- - Windowsの古いバージョン: Windows Vista以前のバージョンでは、`__declspec(thread)` は実行ファイルにリンクされたDLLでのみ正常に動作し、`LoadLibrary()`でロードされるDLLでは正しく機能しない場合があります。
- - TLSの制限: TLSの使用には、メモリの消費量やスレッドの生成数など、いくつかの制限が存在します。詳細については、各プラットフォームのドキュメントを参照してください。
まとめ
スレッド局所記憶(TLS)は、マルチスレッド環境で各スレッドが独立したデータを保持するための重要な技術です。各プラットフォームや言語で異なる実装が存在しますが、基本的な概念は共通しています。TLSを適切に使用することで、スレッド間の競合を避け、より安定したマルチスレッドアプリケーションを開発することができます。