Локальное хранилище потока (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