Lokalny magazyn wątków (TLS)
Magazyn lokalny wątku (TLS) to metoda, za pomocą której każdy wątek w danym procesie wielowątkowym może przydzielać lokalizacje, w których mają być przechowywane dane specyficzne dla wątku. Dynamicznie powiązane (w czasie wykonywania) dane specyficzne dla wątku są obsługiwane za pomocą interfejsu API TLS (TlsAlloc). Win32 i kompilator Microsoft C++ obsługują teraz dane statycznie powiązane (czas ładowania) dla wątku oprócz istniejącej implementacji interfejsu API.
Implementacja kompilatora dla protokołu TLS
C++11: Specyfikator thread_local
klasy magazynu jest zalecanym sposobem określania magazynu lokalnego wątku dla obiektów i składowych klas. Aby uzyskać więcej informacji, zobacz Klasy magazynu (C++).
MSVC udostępnia również atrybut specyficzny dla firmy Microsoft, wątek jako rozszerzony modyfikator klasy magazynu. Użyj słowa kluczowego __declspec
, aby zadeklarować zmienną thread
. Na przykład, poniższy kod deklaruje lokalną zmienną całkowitą wątku i inicjuje ją wartością:
__declspec( thread ) int tls_i = 1;
Reguły i ograniczenia
Podczas deklarowania statycznie powiązanych obiektów lokalnych i zmiennych należy przestrzegać poniższych wytycznych. Te wytyczne dotyczą zarówno wątku , jak i thread_local:
Atrybut
thread
można stosować tylko do deklaracji i definicji klas i danych. Nie można jej używać w deklaracjach funkcji ani definicjach. Na przykład następujący kod generuje błąd kompilatora:__declspec( thread )void func(); // This will generate an error.
Modyfikator
thread
można określić tylko dla elementów danych z zakresemstatic
. Obejmuje to globalne obiekty danych (zarównostatic
iextern
), lokalne obiekty statyczne i statyczne elementy członkowskie danych klas języka C++. Nie można zadeklarować automatycznych obiektów danych za pomocą atrybututhread
. Poniższy kod generuje błędy kompilatora: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; }
Deklaracje i definicja obiektu lokalnego wątku muszą określać
thread
atrybut . Na przykład następujący kod generuje błąd:#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int __declspec( thread )tls_i; // declaration and definition differ.
Atrybut
thread
nie może być używany jako modyfikator typów. Na przykład następujący kod generuje błąd kompilatora:char __declspec( thread ) *ch; // Error
Ponieważ deklaracja obiektów języka C++ używających atrybutu
thread
jest dozwolona, następujące dwa przykłady są semantycznie równoważne:__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.
Adres obiektu lokalnego wątku nie jest uznawany za stały, a żadne wyrażenie obejmujące taki adres nie jest uznawane za wyrażenie stałe. W warstwie Standardowa C efekt polega na tym, aby nie zezwalać na używanie adresu zmiennej lokalnej wątku jako inicjatora dla obiektu lub wskaźnika. Na przykład następujący kod jest oflagowany jako błąd kompilatora języka C:
__declspec( thread ) int tls_i; int *p = &tls_i; //This will generate an error in C.
To ograniczenie nie ma zastosowania w języku C++. Ponieważ język C++ umożliwia dynamiczne inicjowanie wszystkich obiektów, można zainicjować obiekt przy użyciu wyrażenia, które używa adresu zmiennej lokalnej wątku. Odbywa się tak samo jak budowa obiektów lokalnych wątków. Na przykład pokazany wcześniej kod nie generuje błędu podczas kompilowania go jako pliku źródłowego języka C++. Adres zmiennej lokalnej wątku jest prawidłowy tylko tak długo, jak długo wątek, w którym adres został pobrany, nadal istnieje.
Standard C umożliwia inicjowanie obiektu lub zmiennej z wyrażeniem, które obejmuje odwołanie do samego siebie, ale tylko dla obiektów niestatycznego zakresu. Mimo że język C++ zazwyczaj umożliwia takie dynamiczne inicjowanie obiektów z wyrażeniem, które obejmuje odwołanie do samego siebie, tego rodzaju inicjowanie nie jest dozwolone w przypadku obiektów lokalnych wątku. Przykład:
__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++
Wyrażenie
sizeof
, które zawiera inicjowany obiekt, nie reprezentuje odwołania do samego siebie i jest włączone zarówno w języku C, jak i C++.Język C++ nie zezwala na takie dynamiczne inicjowanie danych wątków z powodu możliwych przyszłych ulepszeń lokalnego magazynu wątku.
W systemach operacyjnych Windows przed Windows Vista ma
__declspec( thread )
pewne ograniczenia. Jeśli biblioteka DLL deklaruje dowolne dane lub obiekt jako__declspec( thread )
, może to spowodować usterkę ochrony w przypadku dynamicznego załadowania. Po załadowaniu biblioteki DLL z biblioteką LoadLibrary powoduje awarię systemu za każdym razem, gdy kod odwołuje się do__declspec( thread )
danych. Ponieważ globalna przestrzeń zmiennej dla wątku jest przydzielana w czasie wykonywania, rozmiar tego miejsca jest oparty na obliczeniu wymagań aplikacji oraz wymagania wszystkich bibliotek DLL, które są statycznie połączone. W przypadku używania programuLoadLibrary
nie można rozszerzyć tego miejsca, aby umożliwić używanie zmiennych lokalnych wątku zadeklarowanych za pomocą__declspec( thread )
polecenia . Użyj interfejsów API protokołu TLS, takich jak TlsAlloc, w dll, aby przydzielić protokół TLS, jeśli biblioteka DLL może zostać załadowana przyLoadLibrary
użyciu polecenia .
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla