コンピュータ プログラミングにおいて、スレッド ローカル ストレージ( TLS ) は、スレッドローカルの静的またはグローバルメモリを使用するメモリ管理方法です。この概念により、個別のスレッドを持つシステムでグローバルであるように見えるデータを保存できます。
多くのシステムでは、スレッド ローカル メモリ ブロックのサイズに制限が課せられており、実際、かなり厳しい制限が課せられることがよくあります。一方、システムが少なくともメモリ アドレス(ポインタ) サイズの変数をスレッド ローカルに提供できる場合は、そのようなメモリ ブロックを動的に割り当て、そのブロックのメモリ アドレスをスレッド ローカル変数に格納することで、任意のサイズのメモリ ブロックをスレッド ローカルな方法で使用できるようになります。RISC マシンでは、呼び出し規約によって、この用途のために スレッド ポインタレジスタが予約されることがよくあります。
使用法
現代のプログラミングではグローバル変数の使用は一般に推奨されていませんが、 UNIXなどの一部の古いオペレーティング システムはもともと単一プロセッサハードウェア用に設計されており、重要な値を格納するためにグローバル変数を使用することがよくあります。 一例として、 C ライブラリerrnoの多くの関数で使用されるが挙げられます。 複数のスレッドが変数を変更する可能性のある現代のマシンでは、あるスレッドでのシステム関数の呼び出しにより、別のスレッドでのシステム関数の呼び出しによって以前に設定された値が、おそらくその別のスレッドの後続のコードがエラー状態をチェックする前に上書きされる可能性があります。 解決策は、グローバルであるように見えるが、物理的にはスレッドごとのメモリ プール、つまりスレッド ローカル ストレージに格納される変数を持つことです。
errnoerrno
2 つ目の使用例は、複数のスレッドがグローバル変数に情報を蓄積することです。競合状態を回避するには、このグローバル変数へのすべてのアクセスをmutexで保護する必要があります。代わりに、各スレッドがスレッド ローカル変数に蓄積することで、競合状態の可能性がなくなり、ロックの必要性がなくなります。その後、スレッドは、独自のスレッド ローカル変数から単一のグローバル変数への最終的な蓄積を同期するだけで済みます。
Windows 実装
アプリケーションプログラミング インターフェイス(API) 関数をTlsAlloc使用して、未使用のTLS スロット インデックスを取得できます。その場合、そのTLS スロット インデックスは「使用済み」とみなされます。
TlsGetValue次に、および関数を使用して、 TLS スロット インデックスTlsSetValueで識別されるスレッド ローカル変数にメモリ アドレスを読み書きします。は、現在のスレッドの変数にのみ影響します。関数を呼び出して、TLS スロット インデックスを解放できます。
TlsSetValueTlsFree
各スレッドにはWin32 スレッド情報ブロックがあります。このブロックのエントリの 1 つは、そのスレッドのスレッドローカル ストレージ テーブルです。[1]
の各呼び出しは、TlsAllocこのテーブルへの一意のインデックスを返します。各スレッドは、スレッド独自のテーブルにエントリを設定および検索するため、
TlsSetValue(index, value)を介して指定された値を独立して使用および取得できます。TlsGetValue(index)
TlsXxx 関数ファミリとは別に、Windows 実行可能ファイルは、実行中のプロセスの各スレッドの異なるページにマップされるセクションを定義できます。TlsXxx 値とは異なり、これらのページには任意の有効なアドレスを含めることができます。ただし、これらのアドレスは実行中のスレッドごとに異なるため、非同期関数 (別のスレッドで実行される可能性がある) に渡したり、仮想アドレスがプロセス全体で一意であると想定するコードに渡したりしないでください。TLS セクションはメモリ ページングを使用して管理され、そのサイズはページ サイズ (x86 マシンでは 4kB) に量子化されます。このようなセクションは、プログラムのメイン実行可能ファイル内でのみ定義できます。DLL にはこのようなセクションを含めないでください。LoadLibrary で読み込むときに正しく初期化されないためです。
Pthreads 実装
Pthreads APIでは、スレッドのローカル メモリはスレッド固有のデータという用語で指定されます。
関数と はpthread_key_create、それぞれpthread_key_deleteスレッド固有のデータのキーを作成および削除するために使用されます。キーの型は明示的に不透明のままにされ、 と呼ばれますpthread_key_t。このキーはすべてのスレッドから参照できます。各スレッドでは、 を介してキーをスレッド固有のデータに関連付けることができますpthread_setspecific。データは後で を使用して取得できますpthread_getspecific。
さらに、スレッド固有のデータがNULLpthread_key_createでない場合、スレッド終了時に自動的に呼び出されるデストラクタ関数をオプションで受け入れることができます。デストラクタは、キーに関連付けられた値をパラメータとして受け取り、クリーンアップ アクション (接続を閉じる、メモリを解放するなど) を実行できます。デストラクタが指定されている場合でも、プログラムはプロセス レベルでスレッド固有のデータを解放するために呼び出す必要があります (デストラクタはスレッドのローカル データのみを解放します)。
pthread_key_delete
言語固有の実装
適切な API 関数の呼び出しをプログラマーに任せるだけでなく、プログラミング言語を拡張してスレッド ローカル ストレージ (TLS) をサポートすることも可能です。
C および C++
C11では、 キーワードは_Thread_localスレッドローカル変数の定義に使用されます。 ヘッダー は<threads.h>、サポートされている場合、thread_localそのキーワードの同義語として を定義します。使用例:
#include <threads.h> thread_local int foo = 0 ;
C11では、<threads.h>で始まる名前を使用して、スレッドローカルストレージを取得、変更、破棄するための関数もいくつか定義されていますtss_。C23では、thread_localそれ自体がキーワードになります。[2]
C++11では、以下の場合に使用できる
thread_local[3]キーワードが導入されました。
- 名前空間レベル(グローバル)変数
- ファイルの静的変数
- 関数の静的変数
- 静的メンバー変数
それ以外にも、さまざまなコンパイラ実装では、スレッドローカル変数を宣言するための特定の方法が提供されています。
- Solaris Studio C/C++、[4] IBM XL C/C++、[5] GNU C、[6] llvm-gcc、[7] Clang、[8] Intel C++コンパイラ(Linuxシステム)[9]では次の構文が使用されます。
__thread int number;
- Visual C++、[10] Intel C/C++ (Windowsシステム)、[11] C++Builder、Clang、[12] Digital Mars C++では次の構文が使用されます。
__declspec(thread) int number;
- C++Builder は次の構文もサポートしています:
int __thread number;
VistaおよびServer 2008より前のWindowsバージョンでは、__declspec(thread)DLLが実行ファイルにバインドされている場合にのみ動作し、LoadLibrary()でロードされたDLLでは動作しません(保護違反やデータ破損が発生する可能性があります)。[10]
Common Lisp とその他の方言
Common Lisp は、動的スコープ変数と呼ばれる機能を提供します。
動的変数には、関数の呼び出しとその関数によって呼び出されるすべての子に対してプライベートなバインディングがあります。
この抽象化は、スレッド固有のストレージに自然にマッピングされ、スレッドを提供する Lisp 実装はこれを実行します。Common Lisp には多数の標準動的変数があるため、これらの変数が動的バインディングでスレッドローカルなセマンティクスを持たない限り、スレッドを言語の実装に合理的に追加することはできません。
たとえば、標準変数は*print-base*整数が印刷されるデフォルトの基数を決定します。この変数がオーバーライドされると、囲むすべてのコードは別の基数で整数を印刷します。
;;; 関数 foo とその子は;; 16 進数で
出力します: ( let (( *print-base* 16 )) ( foo ))
関数が異なるスレッドで同時に実行できる場合、このバインディングは適切にスレッドローカルである必要があります。そうでない場合、各スレッドはグローバル印刷基数を誰が制御するかをめぐって争うことになります。
だ
Dバージョン 2では、すべての静的変数とグローバル変数はデフォルトでスレッドローカルであり、他の言語の「通常の」グローバル変数と静的変数に似た構文で宣言されます。グローバル変数は、sharedキーワードを使用して明示的に要求する必要があります。
int threadLocal ; // これはスレッドローカル変数です。shared int global ; // これはすべてのスレッドで共有されるグローバル変数です。
sharedキーワードはストレージクラスとしても、型修飾子としても機能します。共有変数は、静的にデータ整合性を強制するいくつかの制限の対象となります。[13]これらの制限なしで「クラシック」グローバル変数を宣言するには、unsafe __gsharedキーワードを使用する必要があります。[14]
__gshared int global ; // これは単純なグローバル変数です。
ジャワ
Javaでは、スレッドローカル変数はThreadLocal クラスオブジェクトによって実装されます。[15] ThreadLocalはT型の変数を保持し、[15] get/setメソッドを介してアクセスできます。たとえば、整数値を保持するThreadLocal変数は次のようになります。
プライベート静的最終ThreadLocal < Integer > myThreadLocalInteger = new ThreadLocal < Integer > ();
少なくともOracle/OpenJDKの場合、OSスレッドがJavaスレッドの他の側面で使用されているにもかかわらず、ネイティブのスレッドローカルストレージは使用されません。代わりに、各スレッドオブジェクトは、ThreadLocalオブジェクトとその値の(非スレッドセーフな)マップを格納します(各ThreadLocalがThreadオブジェクトと値のマップを持ち、パフォーマンスのオーバーヘッドが発生するのとは対照的です)。[16]
.NET 言語: C# など
C#などの.NET Framework言語では、静的フィールドはThreadStatic属性でマークできます: [17] : 898
クラスFooBar { [ThreadStatic]プライベート静的int _foo ; }
.NET Framework 4.0では、スレッドローカル変数の割り当てと遅延読み込みにSystem.Threading.ThreadLocal<T>クラスが使用できます。[17] : 899
クラスFooBar {プライベート静的System.Threading.ThreadLocal <int> _foo ; }
また、スレッドローカル変数を動的に割り当てるためのAPIも利用可能である。[17] : 899–890
オブジェクトパスカル
Object Pascal ( Delphi ) またはFree Pascalでは、スレッドローカル ストレージを使用して変数を宣言するために、'var' の代わりに予約キーワード threadvar を使用でき ます。
var
mydata_process :整数;スレッドvar mydata_threadlocal :整数;
オブジェクティブC
Cocoa、GNUstep、OpenStepでは、各NSThreadオブジェクトにはスレッドのメソッドを通じてアクセスできるスレッドローカル辞書がありますthreadDictionary。
NSMutableDictionary * dict = [[ NSThread currentThread ] threadDictionary ]; dict [ @"キー" ] = @"データ" ;
パール
Perlでは、スレッドは言語の進化の最終段階で追加されました。これは、既存のコードの大部分がComprehensive Perl Archive Network (CPAN) にすでに存在していた後のことでした。そのため、Perl のスレッドはデフォルトですべての変数に対して独自のローカル ストレージを使用し、既存のスレッド非対応コードに対するスレッドの影響を最小限に抑えます。Perl では、スレッド共有変数は属性を使用して作成できます。
スレッドを使用します。threads ::shared を使用します。
私の$localvar ;私の$sharedvar :共有;
ピュアベーシック
PureBasicでは、スレッド変数はキーワード Threaded を使用して宣言されます。
スレッドVar
パイソン
Pythonバージョン 2.4 以降では、 threadingモジュールのローカルクラスを使用してスレッドローカル ストレージを作成できます。
スレッド
をインポートするmydata = threading.local ( ) mydata.x = 1
ローカルクラスのインスタンスを複数作成して、異なる変数セットを格納することができます。[18]したがって、これはシングルトンではありません。
ルビー
Ruby は[]=/メソッドを使用してスレッドローカル変数を作成/アクセスできます[]:
スレッド. current [ :user_id ] = 1
さび
スレッドローカル変数は、Rust 標準ライブラリによって提供されるマクロ
を使用してRustで作成できます。thread_local!
std :: cell :: RefCellを使用します。std :: thread を使用します。
thread_local! (静的FOO : RefCell < u32 > = RefCell :: new ( 1 ));
FOO . with ( | f | { assert_eq! ( * f .borrow ( )、1 ) ; * f .borrow_mut () = 2 ; });
// 各スレッドは、スレッドローカル値のコピーをすでに 2 に変更しているにもかかわらず、初期値 1 から開始します。let
t = thread :: spawn ( move || { FOO . with ( | f | { assert_eq ! ( * f .borrow ( ), 1 ); * f .borrow_mut ( ) = 3 ; }); });
// スレッドが完了するまで待機し、パニックで終了します
t . join (). unwrap ();
// 子スレッドがそのスレッドの値を 3 に変更したにもかかわらず、元のスレッドは元の値 2 を保持します
FOO . with ( | f | { assert_eq ! ( * f .borrow (), 2 ); });
参照
参考文献
- ^ Pietrek, Matt (2006 年 5 月). 「Under the Hood」. Microsoft Systems Journal . 11 (5). 2010 年 9 月 9 日時点のオリジナルよりアーカイブ。2010年4 月 6 日閲覧。
- ^ 「並行性サポートライブラリ - cppreference.com」。en.cppreference.com。
- ^ C++11標準のセクション3.7.2
- ^ 「Sun の実装に固有の C コンパイラ情報」。Cユーザーズ ガイド Sun Studio 8。2004 年。2.3 スレッド ローカル ストレージ指定子。
- ^ 「XL C/C++ コンパイラ」。2010 年 8 月。スレッドローカル ストレージ (TLS)。2011 年 4 月 11 日時点のオリジナルよりアーカイブ。
- ^ 「スレッドローカルストレージ」。GCC 3.3.1 マニュアル。2003 年。
- ^ 「LLVM 2.0 リリース ノート」。2007 年 5 月 23 日。llvm-gcc の改善。
- ^ 「Clang 言語拡張 - Clang 3.8 ドキュメント」。はじめに。
このドキュメントでは、Clang が提供する言語拡張について説明します。ここでリストされている言語拡張に加えて、Clang は幅広い GCC 拡張をサポートすることを目指しています。これらの拡張の詳細については、GCC マニュアルを参照してください。
- ^ 「Intel® C++ コンパイラー 8.1 Linux 版リリースノート、Intel IA-32 および Itanium® プロセッサー用」(PDF) 。2004 年。スレッドローカル ストレージ。2015年 1 月 19 日時点のオリジナル(PDF)からのアーカイブ。
- ^ ab Visual Studio 2003: 「スレッド ローカル ストレージ (TLS)」。Microsoft Docs。2017年 6 月 5 日。
- ^ Intel C++ コンパイラ 10.0 (Windows): スレッドローカル ストレージ
- ^ 「Clang の属性 - Clang 3.8 ドキュメント」スレッド。
- ^ Alexandrescu, Andrei (2010 年 7 月 6 日). 「第 13 章 - 並行性」. D プログラミング言語. InformIT. p. 3. 2014 年1 月 3 日閲覧。
- ^ Bright, Walter (2009 年 5 月 12 日). 「Migrating to Shared」. dlang.org . 2014 年1 月 3 日閲覧。
- ^ ab Bloch 2018、p. 151-155、§項目33: 型安全な異種コンテナを検討する。
- ^ 「Java の ThreadLocal は内部的にどのように実装されているのか?」Stack Overflow . Stack Exchange . 2015 年12 月 27 日閲覧。
- ^ abc アルバハリ2022。
- ^ 「cpython/Lib/_threading_local.py at 3.12 · python/cpython」。GitHub 。2023年10月25日閲覧。
文献
- アルバハリ、ジョセフ (2022)。C # 10 in a Nutshell (初版)。オライリー。ISBN 978-1-098-12195-2。
- Bloch, Joshua (2018)。『Effective Java: プログラミング言語ガイド』(第 3 版)。Addison- Wesley。ISBN 978-0134685991。
外部リンク
- スレッドローカル ストレージの ELF 処理 — CまたはC++での実装に関するドキュメント。
- ACE_TSS< TYPE > クラステンプレートリファレンス
- RWTThreadLocal<Type> クラス テンプレートのドキュメント
- Doug Doedens による記事「スレッド固有のデータを渡すためにスレッドローカル ストレージを使用する」
- 「スレッドローカルストレージ」Lawrence Crowl 著
- 記事「共有することが必ずしも良いこととは限らない」ウォルター・ブライト
- Java での ThreadLocal の実践的な使用法: http://www.captechconsulting.com/blogs/a-persistence-pattern-using-threadlocal-and-ejb-interceptors
- GCC "[1]"
- 錆び "[2]"
