Lokální úložiště vláken (TLS)
Místní úložiště vlákna (TLS) je metoda, podle které každé vlákno v daném vícevláknovém procesu může přidělit umístění, ve kterých se mají ukládat data specifická pro vlákna. Dynamicky svázaná data specifická pro vlákna (run-time) se podporují prostřednictvím rozhraní TLS API (TlsAlloc). Win32 a kompilátor Microsoft C++ teď kromě stávající implementace rozhraní API podporují staticky svázaná data (doba načítání) pro každé vlákno.
Implementace kompilátoru pro protokol TLS
C++11: Specifikátor thread_local
třídy úložiště je doporučený způsob, jak určit místní úložiště vláken pro objekty a členy třídy. Další informace najdete v tématu Třídy úložiště (C++).
MSVC také poskytuje atribut specifický pro Microsoft, vlákno, jako modifikátor rozšířené třídy úložiště. Pomocí klíčového __declspec
slova deklarujte proměnnou thread
. Například následující kód deklaruje místní proměnnou celočíselného vlákna a inicializuje ji hodnotou:
__declspec( thread ) int tls_i = 1;
Pravidla a omezení
Při deklarování staticky vázaného vlákna místních objektů a proměnných musí být pozorovány následující pokyny. Tyto pokyny platí pro vlákno i pro thread_local:
Atribut
thread
lze použít pouze u deklarací tříd a dat a definic. Nedá se použít u deklarací funkcí ani definic. Například následující kód vygeneruje chybu kompilátoru:__declspec( thread )void func(); // This will generate an error.
thread
Modifikátor lze zadat pouze u datových položek s rozsahemstatic
. To zahrnuje globální datové objekty (astatic
extern
), místní statické objekty a statické datové členy tříd jazyka C++. Automatické datové objekty nelze deklarovat pomocí atribututhread
. Následující kód generuje chyby kompilátoru:void func1() { __declspec( thread )int tls_i; // This will generate an error. } int func2(__declspec( thread )int tls_i ) // This will generate an error. { return tls_i; }
Deklarace a definice místního objektu vlákna musí všechny specifikovat
thread
atribut. Například následující kód vygeneruje chybu:#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int __declspec( thread )tls_i; // declaration and definition differ.
Atribut
thread
nelze použít jako modifikátor typu. Například následující kód vygeneruje chybu kompilátoru:char __declspec( thread ) *ch; // Error
Vzhledem k tomu, že deklarace objektů jazyka C++, které používají
thread
atribut, je povolena, jsou následující dva příklady sémanticky ekvivalentní:__declspec( thread ) class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; __declspec( thread ) B BObject; // OK--BObject is declared thread local.
Adresa místního objektu vlákna není považována za konstantní a žádný výraz zahrnující takovou adresu se nepovažuje za konstantní výraz. Ve standardním jazyce C je výsledkem zakázání použití adresy místní proměnné vlákna jako inicializátoru objektu nebo ukazatele. Například následující kód je označen jako chyba kompilátoru jazyka C:
__declspec( thread ) int tls_i; int *p = &tls_i; //This will generate an error in C.
Toto omezení neplatí v jazyce C++. Vzhledem k tomu, že jazyk C++ umožňuje dynamickou inicializaci všech objektů, můžete objekt inicializovat pomocí výrazu, který používá adresu místní proměnné vlákna. Dělá se stejně jako konstrukce místních objektů vlákna. Například výše uvedený kód negeneruje chybu, když je zkompilován jako zdrojový soubor C++. Adresa místní proměnné vlákna je platná pouze za předpokladu, že vlákno, ve kterém byla adresa přijata, stále existuje.
Standardní jazyk C umožňuje inicializaci objektu nebo proměnné pomocí výrazu, který zahrnuje odkaz na sebe sama, ale pouze pro objekty nestatického rozsahu. I když C++ obecně umožňuje takovou dynamickou inicializaci objektů pomocí výrazu, který zahrnuje odkaz na sebe, tento druh inicializace není povolen s místními objekty vlákna. Příklad:
__declspec( thread )int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C __declspec( thread )int tls_i = sizeof( tls_i ) // Legal in C and C++
Výraz
sizeof
, který obsahuje inicializovaný objekt, nepředstavuje odkaz na sebe a je povolen v jazyce C i C++.Jazyk C++ neumožňuje takovou dynamickou inicializaci dat vlákna kvůli možným budoucím vylepšením místního úložiště vlákna.
V operačních systémech Windows před Windows Vista
__declspec( thread )
má určitá omezení. Pokud knihovna DLL deklaruje jakákoli data nebo objekt jako__declspec( thread )
, může způsobit chybu ochrany, pokud je dynamicky načtena. Po načtení knihovny DLL s LoadLibrary způsobí selhání systému pokaždé, když kód odkazuje na__declspec( thread )
data. Vzhledem k tomu, že globální proměnný prostor pro vlákno je přidělen za běhu, velikost tohoto prostoru je založená na výpočtu požadavků aplikace a požadavků všech knihoven DLL, které jsou staticky propojené. Pokud použijeteLoadLibrary
, nemůžete rozšířit toto místo tak, aby umožňovalo místní proměnné vlákna deklarované pomocí__declspec( thread )
. Pomocí rozhraní TLS API, jako je například TlsAlloc, v knihovně DLL přidělte protokol TLS, pokud může být knihovna DLL načtenaLoadLibrary
.