Локальное хранилище потока (TLS)

Локальное хранилище потока (TLS) — это механизм, с помощью которого каждый поток в указанном многопоточном процессе может выделять расположения для хранения данных определенного потока. Динамически привязанные (время выполнения) данные, относящиеся к потоку, поддерживаются через API TLS (TlsAlloc). Win32 и компилятор Microsoft C++ теперь поддерживают статически привязанные (время загрузки) данные для каждого потока в дополнение к существующей реализации API.

Реализация компилятора для TLS

C++11.thread_local Описатель класса хранилища — это рекомендуемый способ указать локальное хранилище потока для объектов и членов класса. Дополнительные сведения см. в разделе служба хранилища классов (C++).

MSVC также предоставляет атрибут, поток, определенный корпорацией Майкрософт, как модификатор расширенного класса хранилища. __declspec Используйте ключевое слово для объявления переменнойthread. В следующем примере кода показано, как объявлять целочисленную локальную переменную потока и инициализировать её некоторым значением:

__declspec( thread ) int tls_i = 1;

Правила и ограничения

При объявлении статистически связываемых локальных объектов и переменных потока необходимо соблюдать следующие рекомендации. Эти рекомендации применяются как к потоку, так и к thread_local:

  • Атрибут thread можно применять только к объявлениям и определениям классов и данных. Его нельзя использовать для объявлений или определений функций. Например, следующий код вызовет ошибку компиляции:

    __declspec( thread )void func();     // This will generate an error.
    
  • thread Модификатор можно указать только для элементов данных с static экстентом. Это включает глобальные объекты данных (как и ), локальные статические объекты, так static и externстатические элементы данных классов C++. Автоматические объекты данных не могут быть объявлены атрибутом thread . Следующий код вызывает ошибки компилятора:

    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;
    }
    
  • Объявления и определение локального объекта потока должны указывать thread атрибут. Например, следующий код вызывает ошибку:

    #define Thread  __declspec( thread )
    extern int tls_i;        // This will generate an error, since the
    int __declspec( thread )tls_i;        // declaration and definition differ.
    
  • Атрибут thread нельзя использовать в качестве модификатора типа. Например, следующий код вызовет ошибку компиляции:

    char __declspec( thread ) *ch;        // Error
    
  • Так как объявление объектов C++, использующих thread атрибут, разрешено, следующие два примера семантически эквивалентны:

    __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.
    
  • Адрес локального объекта потока не считается константой, и любое выражение, связанное с таким адресом, не считается константным выражением. В стандартном C эффект заключается в запрете использования адреса локальной переменной потока в качестве инициализатора для объекта или указателя. Например, компилятор C отмечает следующий код как ошибочный:

    __declspec( thread ) int tls_i;
    int *p = &tls_i;       //This will generate an error in C.
    

    Это ограничение не применяется в C++. Так как C++ допускает динамическую инициализацию всех объектов, можно инициализировать объект с помощью выражения, которое использует адрес локальной переменной потока. Это делается так же, как создание локальных объектов потока. Например, код, показанный ранее, не создает ошибку при компиляции в виде исходного файла C++. Адрес локальной переменной потока действителен, только если поток, в котором был взят адрес, по-прежнему существует.

  • Стандарт C позволяет инициализации объекта или переменной с выражением, которое включает ссылку на себя, но только для объектов нестатической степени. Хотя C++ обычно допускает такую динамическую инициализацию объектов с выражением, которое включает ссылку на себя, такой тип инициализации не допускается с локальными объектами потока. Например:

    __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++
    

    sizeof Выражение, включающее инициализированный объект, не представляет ссылку на себя и включается как в C, так и в C++.

    C++ не разрешает такую динамическую инициализацию данных потока из-за возможных будущих улучшений локального хранилища потока.

  • В операционных системах Windows до Windows Vista __declspec( thread ) есть некоторые ограничения. Если библиотека DLL объявляет любые данные или объект как __declspec( thread ), это может привести к сбою защиты при динамической загрузке. После загрузки библиотеки DLL с помощью LoadLibrary происходит сбой системы, когда код ссылается на __declspec( thread ) данные. Поскольку пространство глобальных переменных для потока выделяется во время выполнения, размер данного пространства основан на расчете требований приложению, а также требований всех библиотек DLL, которые привязываются статически. При использовании LoadLibraryвы не можете расширить это пространство, чтобы разрешить локальные переменные потока, объявленные с __declspec( thread )помощью . Используйте API TLS, такие как TlsAlloc, в библиотеке DLL, чтобы выделить TLS, если библиотека DLL может быть загружена.LoadLibrary

См. также

Реализация многопоточности на языке C с помощью функций Win32