Pamięć lokalna wątku

Pamięć lokalna wątku (ang. Thread-local storage, TLS) – rozwiązanie używane w programowaniu komputerowym przypisującym pamięć statyczną lub globalną lokalnie do wątku.

Takie rozwiązanie bywa niezbędne, ponieważ wszystkie wątki w procesie współdzielą tą samą przestrzeń adresową. Innymi słowy, odnosząc się do zmiennej statycznej lub globalnej, różne wątki tego samego procesu odwołują się do tego samego miejsca w pamięci. Zmienne na stosie są natomiast lokalne dla wątku, ponieważ każdy wątek posiada własny stos, rezydujący w innym miejscu pamięci.

Czasami jest jednak pożądane, aby dwa wątki odnosząc się do tej samej zmiennej statycznej lub globalnej odwoływały się do różnych obszarów pamięci. To wymaga uczynienia zmiennej lokalną dla wątku. Klasycznym przykładem jest w języku C przechowująca kod błędu zmienna errno.

Jeżeli znany jest co najmniej rozmiar wskaźników, jest zasadniczo możliwe stworzenie bloków pamięci lokalnej wątku o dowolnie określonym rozmiarze. Alokuje się wówczas odpowiednie bloki pamięci, a wskaźniki do nich przechowuje w przestrzeni lokalnej wątku.

Implementacja Windows edytuj

Funkcja API TlsAlloc może być użyta do uzyskania nieużywanego indeksu slotu TLS (TLS slot index). Indeks slotu TLS zostanie wówczas oznaczony jako ‘używany’.

Funkcje TlsGetValue i TlsSetValue mogą być użyte do odczytu i zapisu adresu pamięci do zmiennej lokalnej wątku identyfikowanej przez indeks slotu TLS. TlsSetValue może wpływać jedynie na zmienne aktualnego wątku.

Wywołanie funkcji TlsFree zwalnia indeks slotu TLS. Indeks zostanie wówczas oznaczony jako ‘nieużywany’ i kolejne wywołanie TlsAlloc może zwrócić go ponownie.

Implementacja Pthreads edytuj

Obsługa TLS w Pthreads (w nomenklaturze Pthreads Thread-Specific Data – dane specyficzne dla wątku) jest zbliżona do TlsAlloc i oferuje podobną funkcjonalność co Windows. pthread_key_create tworzy klucz, wraz z opcjonalnym destruktorem, który może być później powiązany z danymi specyficznymi dla wątku przez pthread_setspecific. Dane mogą być uzyskane przez wywołanie pthread_getspecific. Jeżeli wartość specyficzna dla wątku nie jest pusta (NULL), przy zamykaniu wątku zostanie wywołany destruktor. Dodatkowo, klucz musi zostać zniszczony przez pthread_key_delete.

Implementacja specyficzna dla języka edytuj

Zamiast wymagać od programistów korzystania z odpowiednich funkcji API, jest również możliwe rozszerzenie języka programowania aby obsługiwał TLS.

Object Pascal edytuj

W językach Delphi oraz Free Pascal istnieje możliwość użycia słowa kluczowego 'threadvar' zamiast 'var', w celu zadeklarowania zmiennych przechowywanych w pamięci lokalnej wątku.

var
   mydata_process: integer;
threadvar
   mydata_threadlocal: integer;

Java edytuj

W języku Java zmienne lokalne wątku są implementowane przez klasę ThreadLocal. Obiekt ThreadLocal utrzymuje oddzielne instancje zmiennych dla każdego wątku, który wywoła na nim metodę get lub set. Poniższy przykład (dla J2SE 5.0 lub nowszych) ilustruje użycie obiektu ThreadLocal przechowującego obiekt Integer:

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

Powyższy kod deklaruje i tworzy obiekt local klasy ThreadLocal który zwraca początkową wartość 1 jeżeli żadna inna wartość nie została zapisana przez wywołujący ją wątek. Poniższy kod zwiększa wartość przechowywanej wartości lokalnie dla wywołującego wątku.

local.set( local.get()+1 );

Metoda local.get() zwraca aktualną wartość typu Integer związaną z aktualnym wątkiem, lub 1 jeżeli żaden obiekt nie został powiązany z wątkiem. Kod wywołuje metodę local.set() aby ustawić nową wartość powiązaną z wątkiem. (Proszę zauważyć, że powyższy przykład używa generics oraz autoboxing—elementów dodanych do Javy w wersji J2SE 5.0.)

Sun Studio C/C++, IBM XL C/C++, GNU C oraz Intel C/C++ (systemy Linux) edytuj

Słowo kluczowe __thread jest używane jak poniżej:

__thread int liczba;
  • __thread definiuje liczba jako zmienną lokalną dla wątku.
  • int definiuje typ zmiennej liczba jako int.

Visual C++, Intel C/C++ (systemy Windows) edytuj

W Visual C++ słowo kluczowe declspec(thread) jest używane jak poniżej::

__declspec(thread) int liczba;
  • __declspec(thread) definiuje liczba jako zmienną lokalną dla wątku.
  • int definiuje typ zmienej liczba jako int.
  • Na systemach Windows starszych niż Vista oraz Server 2008 __declspec(thread) działa w bibliotekach DLL tylko gdy są one powiązane statycznie z plikiem wykonywalnym i nie zadziała z bibliotekami ładowanymi przez LoadLibrary() (może wystąpić błąd ochrony lub uszkodzenia danych).
  • Są również dodatkowe zasady: "Zasady i ograniczenia TLS" (j. ang) w MSDN.

Digital Mars C++ edytuj

W kompilatorze Digital Mars C++ słowo kluczowe declspec(thread) jest używane jak poniżej:

__declspec(thread) int liczba;
  • __declspec(thread) definiuje liczba jako zmienną lokalną dla wątku.
  • int definiuje typ zmiennej liczba jako int.

D edytuj

W języku programowania D w wersji 2, wszystkie statyczne i globalne zmienne są domyślnie lokalne dla wątku i deklarowane podobnie jak "zwykłe" globalne i statyczne zmienne w innych językach. Stworzenie zmiennej globalnej dla wszystkich wątków musi być jawnie zadeklarowane przez użycie słowa kluczowego shared lub __gshared:

int threadLocal; // To jest zmienna globalna lokalna dla wątku. Jej typ to int.
shared int globalTyped; // To jest zmienna globalna dla wszystkich wątków. Jej typ to shared(int).
__gshared int global; // To jest zmienna globalna dla wszystkich wątków. Jej typ to int.

Słowo kluczowe __gshared jest poprzedzone dwoma podkreślnikami w celu ograniczenia jego użycia. Standardową metodą jest użycie słowa shared, które modyfikuje typ zmiennej. __gshared zostało wprowadzone w celu uniknięcia zmiany typu zmiennej i odpowiada ono deklaracji zmiennej globalnej w języku C/C++.

Borland C++ Builder edytuj

W Borland C++ Builder słowo kluczowe __declspec(thread) jest używane jak poniżej:

__declspec(thread) int liczba;

To samo w bardziej elegancki sposób:

int __thread liczba;
  • __declspec(thread) definiuje liczba jako zmienną lokalną dla wątku. __thread jest synonimem __declspec(thread).
  • int definiuje typ zmiennej liczba jako int.

GCC edytuj

GCC C/C++ implementuje __thread jak podano wyżej.

Wartość inicjująca zmienną musi być stałą czasu kompilacji, nawet w C++. Na przykład.

__thread int liczba = 1;

ale nie

void f(int liczba)
{
 static __thread int liczba_kopia = liczba;

lub (C++)

__thread int liczba = oblicz_liczbę();

C# i inne języki .NET edytuj

Statyczne pola mogą być oznaczane przez atrybut ThreadStatic:

class FooBar
{
  [ThreadStatic] static int foo;
}

Jest też dostępne API dla dynamicznego alokowania zmiennych lokalnych dla wątku.

Python edytuj

W języku Python w wersjach 2.4 i późniejszych, klasa local w module threading służy do tworzenia zmiennych lokalnych dla wątku.

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

Ruby edytuj

W języku Ruby zmienne lokalne dla wątku są tworzone i dostępne przy użyciu metod []=/[].

Thread.current[:user_id] = 1

Perl edytuj

W języku Perl wątki zostały dodane w trakcie ewolucji języka, gdy duża ilość istniejącego kodu była już obecna w Comprehensive Perl Archive Network. W wyniku tego, wątki w Perlu mają domyślne swój własny obszar pamięci lokalnej dla wszystkich zmiennych, w celu zminimalizowania skutków zetknięcia się wątków z istniejącym, nie zabezpieczonym wątkowo kodem. W Perlu, zmienne współdzielone między wątkami mogą zostać utworzone poprzez atrybut:

use threads;
use threads::shared;

my $localvar;
my $sharedvar :shared;

Linki zewnętrzne edytuj