Udostępnij przez


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 zakresem static . Obejmuje to globalne obiekty danych (zarówno static i extern), lokalne obiekty statyczne i statyczne elementy członkowskie danych klas języka C++. Nie można zadeklarować automatycznych obiektów danych za pomocą atrybutu thread . 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 programu LoadLibrarynie 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 przy LoadLibraryużyciu polecenia .

Zobacz też

Wielowątkowość z językiem C i podsystemem Win32