Thread Local Storage

Firul de Stocare locală ( TLS ) sau memorie fir locale , este un tip de memorie specific și local , la un fir .

Acest mecanism este uneori necesar deoarece toate firele din același proces au același spațiu de adrese . Prin urmare, datele localizate într-o variabilă statică sau globală se află exact în aceeași locație de memorie pentru toate firele și, prin urmare, corespunde aceleiași entități.

Cele Variabilele de pe stivă sunt totuși locale la firul, pentru că fiecare fir are propria stiva, separată de cea a altor fire.

Cu toate acestea, uneori este util ca două fire să poată face referire la aceeași variabilă „globală”, în timp ce fiecare are o copie separată, deci la adrese de memorie diferite. Acest lucru face ca variabila să fie „locală” la fir, având în același timp o sintaxă de utilizare identică cu cea a unei variabile globale. Un exemplu banal al acestei variabile a este, de exemplu, variabila errnoa limbajului C .

Dacă este posibil să se creeze cel puțin un pointer local de thread, atunci este posibil să se creeze pentru fiecare thread al sistemului o zonă de memorie de dimensiuni arbitrare care să conțină date de thread locale. Într-adevăr, această zonă poate fi ea însăși o simplă matrice de indicatori, ceea ce face posibilă (prin dereferențierea succesivă) obținerea unui TLS de dimensiuni total arbitrare, indiferent de limita inițială a zonei.

Desen

Procesul de aici are două fire. Alocăm două sloturi TLS: primul pentru a stoca un număr întreg (index 2), al doilea pentru a stoca un indicator (index 4), în albastru deschis. Fiecare fir va obține apoi o zonă de memorie privată (părți verzi închise pentru firul 1, albastru închis pentru firul 2), permițând accesul la datele sale locale, posibil printr-o direcție indirectă pentru pointer, utilizând în același timp doar un index global și identic între cele două (simplificarea codului).

Implementări dependente de sistemul de operare

Implementare pe Windows

În API-ul Windows , se folosește termenul TLS .

TlsAlloc Funcția este utilizată pentru a obține un neutilizat index fantă TLS - în practică, prima disponibilă - care va fi rezervat până la sfârșitul procesului sau eliberarea TLS . Un singur thread trebuie să apeleze această funcție: după aceea, se folosește numai indexul returnat. Această operație este, desigur, complet atomică. Acest indice este, în general, furnizat apoi firelor, fie printr-o variabilă globală (acces numai în citire de către fire), fie în parametrii de creare a firelor (soluție preferabilă). Indicele returnat este un număr întreg, dar nu trebuie considerat un index de matrice. Este necesar să-l tratați ca pe un tip opac .

Funcțiile TlsGetValue și TlsSetValue sunt apoi utilizate pentru (respectiv) citirea și scrierea unei variabile TLS , identificată prin indexul său de slot TLS . Această variabilă este un pointer netipat, a cărui utilizare este gratuită și care va fi specifică fiecărui fir. Cu toate acestea, orice fir este responsabil pentru alocarea datelor vizate de acest indicator.
Se poate observa că dimensiunea acestui indicator depinde de arhitectura Windows curentă ( 32 de biți sau 64 de biți ) și că nu este contraindicat să folosiți slotul pentru a stoca orice variabilă, de dimensiuni mai mici sau egală cu cea a indicatorului , în loc de un indicator.
Aceasta acoperă, în special, cazul unui număr întreg: acesta este modul în care API-ul Windows stochează ultimul cod de eroare obținut de GetLastError , de exemplu.

Funcția TlsFree poate fi utilizată pentru a elibera indexul slotului TLS trecut ca parametru. Indicele este apoi considerat din nou „neutilizat” și poate fi realocat ulterior. Prin urmare, este crucial să fiți siguri, atunci când eliberați, că nu mai există fire care utilizează acest index TLS .

Implementare sub Pthreads (Linux)

În API-ul Pthreads , termenul TSD ( Thread-Specific Data ) este utilizat pentru a indica TLS .

Principiul este similar cu cel utilizat în Windows, doar numele funcțiilor se schimbă:

  1. pthread_key_create este echivalent cu TlsAlloc .
  2. pthread_getspecific este echivalent cu TlsGetValue .
  3. pthread_setspecific este echivalent cu TlsSetValue .
  4. pthread_key_delete este echivalent cu TlsFree .
  5. Cheia ( cheie ) este echivalentă cu TLS sloturi de index , dar este definit în mod explicit printr - un tip de opac pthread_key_t .

Singura diferență reală dintre cele două sisteme este că pthread_key_create permite definirea unui destructor opțional care va fi apelat automat la sfârșitul firului. Fiecare destructor va primi, ca parametru, conținutul stocat în cheia asociată, făcând astfel posibilă eliberarea resurselor asociate.

Utilizarea acestui destructor nu elimină necesitatea de a apela în mod explicit pthread_key_delete pentru a elibera TSD în sine, la nivel de proces. Distructorul permite doar eliberarea datelor locale la fir.

Implementări specifice

În plus față de posibilitatea de a apela funcțiile native ale sistemului de operare descrise mai sus, anumite limbaje sau compilatoare fac posibilă utilizarea unei funcționalități echivalente sau chiar identice cu TLS într-un mod mai simplu și / sau mai practic decât apelarea la primitive de sistem .

Compilatoare și IDE

Compilator Visual C ++ și Intel C ++ (Windows)

Cuvântul cheie __declspec (fir) este utilizat în prefixul declarației:

int variable_globale; __declspec(thread) int variable_TLS ;

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Sun Studio , IBM XL C / C ++ și Intel C ++ Compiler (Linux)

Cuvântul cheie __thread este utilizat ca prefix de declarație:

int variable_globale ; __thread int variable_TLS ;

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Digital Mars C ++

__Declspec (fir) cuvântul cheie este utilizat ca prefix declarație:

int variable_globale ; __declspec(thread) int variable_TLS ;

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Borland C ++ Builder (Windows)

__Declspec (fir) cuvântul cheie este utilizat ca prefix declarație:

int variable_globale ; __declspec(thread) int variable_TLS ;

Cuvântul cheie __thread este o declarație alternativă, dar între tip și identificator:

int variable_globale ; int __thread variable_TLS ;

În ambele cazuri, variabila TLS poate fi apoi utilizată într-un mod complet normal.

GCC / G ++

Cuvântul cheie __thread este utilizat ca prefix de declarație, dar este implementat într-un mod special:

int variable_globale ; __thread int variable_TLS = 1 ;

Cu toate acestea, variabila TLS trebuie imperializată inițializată cu o constantă cunoscută în momentul compilării (cazul exemplului de mai sus).

Nu este permisă declararea unei variabile TLS fără inițializare sau inițializare de un parametru și / sau un apel funcțional.

C ++ 11

De la standardul C ++ 11, limba oferă clasa de stocare thread_local .

Ca și în cazul implementărilor specifice compilatorului, cuvântul cheie este utilizat ca prefix de declarație:

int variable_global ; thread_local int variable_TLS ;

Limbi

Delphi și Free Pascal

Cuvântul cheie ThreadVar este utilizat în locul tradiționalului Var pentru a declara o variabilă TLS .

Var variable_globale : Integer ; ThreadVar variable_TLS : Integer ;

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Java

În Java, variabilele TLS sunt implementate prin intermediul clasei ThreadLocal (en) . Un obiect ThreadLocalmenține o instanță separată a variabilei pentru fiecare fir care apelează accesorii obiectului ( getși set).

Exemplul de mai jos arată cum să creați o variabilă întreagă TLS  :

ThreadLocal<Integer> variable_TLS = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 1; } } ;

Utilizarea variabilei se face prin accesori. De exemplu, un increment:

variable_TLS.set( variable_TLS.get() + 1 ) ; D

În D (versiunea 2), toate variabilele statice și globale sunt, în mod implicit, locale pentru fire și sunt declarate variabile „normale” pentru alte limbi. Este declarația explicită a unei variabile globale „partajate” care necesită utilizarea unui anumit cuvânt cheie, __gshared .

int variable_TLS ; __gshared int variable_globale ;

Variabila TLS poate fi apoi utilizată într-un mod complet normal, precum și variabila globală declarată explicit.

Limbaje C # și .NET

Atribut folosit ThreadStatic (în)  :

class classe_avec_TLS { [ThreadStatic] static int variable_TLS ; }

De asemenea, poate aloca în mod dinamic variabilele TLS prin API Thread.GetNamedDataSlot (în) .

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Piton

În Python (versiunea 2.4 sau mai mare), folosim clasa locală a modulului de filetare pentru a defini o variabilă TLS .

import threading variable_TLS = threading.local() variable_TLS.x = 1

Variabila TLS poate fi apoi utilizată într-un mod complet normal.

Rubin

În Ruby, o variabilă TLS este creată și utilizată datorită metodelor []=/[].

Thread.current[:index_TLS] = 1 Perl

Suportul pentru fire a venit târziu în limba Perl, după ce o cantitate mare de cod sursă a fost introdusă în rețeaua de arhivă Comprehensive . Ca urmare, firele din Perl își creează propriul TLS pentru toate variabilele în mod implicit , astfel încât să minimizeze impactul thread-urilor asupra codului existent care nu este sigur . În Perl, o variabilă partajată între fire (majuscule „normale” în alte limbi) este creată folosind un atribut:

use threads; use threads::shared; my $variable_TLS; my $variable_globale :shared;

linkuri externe

Referințe

  1. Thread Local Storage este MSDN Library (en)
  2. pagină de manual PTHREAD_SPECIFIC este pagini de manual Linux (în)