Введение

Метод управления памятью компьютера

В компьютерном программировании локальное хранилище потоков (TLS) — это метод управления памятью, использующий статическую или глобальную память, локальную для потока. Эта концепция позволяет хранить данные, которые кажутся глобальными в системе с несколькими потоками. Многие системы накладывают ограничения на размер блока локальной памяти потока, зачастую довольно строгие. Однако, если система может предоставить хотя бы переменную размером с адрес памяти (указатель) для локального хранения потока, то это позволяет использовать блоки памяти произвольного размера в локальном для потока режиме, динамически выделяя такой блок памяти и сохраняя адрес этого блока в локальной переменной потока. На RISC-машинах соглашение о вызовах часто резервирует регистр указателя потока для этих целей.

Использование

В то время как использование глобальных переменных обычно не рекомендуется в современном программировании, некоторые старые операционные системы, такие как UNIX, первоначально разрабатывались для однопроцессорного оборудования и часто использовали глобальные переменные для хранения важных значений. Примером является `errno`, используемый многими функциями библиотеки C. На современной машине, где несколько потоков могут изменять переменную `errno`, вызов системной функции в одном потоке может перезаписать значение, ранее установленное вызовом системной функции в другом потоке, возможно, до того, как последующий код в этом другом потоке успеет проверить наличие ошибки. Решением является организация `errno` как переменной, которая выглядит глобальной, но физически хранится в пуле памяти, выделенном для каждого потока – в локальном хранилище потоков. Другой случай использования – когда несколько потоков накапливают информацию в глобальную переменную. Чтобы избежать состояния гонки, каждый доступ к этой глобальной переменной должен быть защищен мьютексом. Вместо этого каждый поток может накапливать данные в локальную переменную потока, тем самым полностью исключая возможность состояния гонки и устраняя необходимость в блокировках. Затем потокам потребуется только синхронизировать окончательное суммирование данных из своей локальной переменной потока в единую глобальную переменную.

Внедрение Windows

Функция TlsAlloc интерфейса прикладного программирования (API) может быть использована для получения неиспользованного индекса слота TLS; этот индекс слота TLS будет считаться "занятым". Функции TlsGetValue и TlsSetValue затем используются для чтения и записи адреса памяти в переменную, локальную для потока, идентифицированную индексом слота TLS. TlsSetValue влияет только на переменную в текущем потоке. Функцию TlsFree можно вызвать для освобождения индекса слота TLS. Для каждого потока существует блок информации Win32. Одной из записей в этом блоке является таблица локального хранения потока для данного потока. TlsAlloc возвращает индекс в эту таблицу, уникальный для каждого адресного пространства, при каждом вызове. Каждый поток имеет собственную копию таблицы локального хранения потока. Следовательно, каждый поток может независимо использовать TlsSetValue(index) и получать указанное значение с помощью TlsGetValue(index), поскольку эти функции устанавливают и извлекают запись из собственной таблицы потока. Помимо семейства функций TlsXxx, исполняемые файлы Windows могут определять раздел, который отображается на отдельную страницу для каждого потока выполняющегося процесса. В отличие от значений TlsXxx, эти страницы могут содержать произвольные допустимые адреса. Однако эти адреса различны для каждого выполняющегося потока и поэтому не должны передаваться асинхронным функциям (которые могут выполняться в другом потоке) или передаваться другим компонентам кода, предполагающим, что виртуальный адрес уникален во всем процессе. Секции TLS управляются посредством страничной организации памяти, а их размер квантуется до размера страницы (4 КБ на машинах x86). Такие секции могут быть определены только в основном исполняемом файле программы. DLL не должны содержать такие секции, поскольку они не инициализируются корректно при загрузке с помощью LoadLibrary.

Внедрение птредов

В API Pthreads память, локальная для потока, обозначается термином "Thread specific data" (данные, специфичные для потока). Функции `pthread_key_create` и `pthread_key_delete` используются соответственно для создания и удаления ключа для данных, специфичных для потока. Тип ключа явно оставлен непрозрачным и называется `pthread_key_t`. Этот ключ виден всем потокам. В каждом потоке ключ может быть связан с данными, специфичными для потока, с помощью `pthread_setspecific`. Данные могут быть впоследствии получены с помощью `pthread_getspecific`. Кроме того, `pthread_key_create` может опционально принимать функцию-деструктор, которая будет автоматически вызвана при завершении потока, если данные, специфичные для потока, не равны NULL. Деструктор получает значение, связанное с ключом, в качестве параметра, чтобы выполнить действия по очистке (закрытие соединений, освобождение памяти и т.п.). Даже если указан деструктор, программа все равно должна вызвать `pthread_key_delete`, чтобы освободить данные, специфичные для потока, на уровне процесса (деструктор освобождает только данные, локальные для потока).

Языковые реализации

Помимо вызова соответствующих функций API программистами, также возможно расширить язык программирования для поддержки локального хранилища потоков (TLS).