Jump to content

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

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

Многие системы накладывают ограничения на размер блока локальной памяти потока, причем зачастую это довольно жесткие ограничения. С другой стороны, если система может предоставить, по крайней мере, локальную для потока переменную размером с адрес памяти (указатель), то это позволяет использовать блоки памяти произвольного размера локальным для потока способом, динамически распределяя такой блок памяти и сохраняя его. адрес памяти этого блока в локальной переменной потока. На RISC машинах соглашение о вызовах часто резервирует для этого использования регистр указателя потока .

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

[ редактировать ]

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

Вторым вариантом использования может быть несколько потоков, накапливающих информацию в глобальной переменной. Чтобы избежать состояния гонки , каждый доступ к этой глобальной переменной должен быть защищен мьютексом . Вместо этого каждый поток может накапливаться в локальной переменной потока, тем самым исключая любую возможность состояния гонки и тем самым устраняя необходимость в блокировке. Затем потокам остается только синхронизировать окончательное накопление из своей собственной локальной переменной потока в одну глобальную переменную.

Реализация Windows

[ редактировать ]

Функция интерфейса прикладного программирования (API) TlsAlloc может использоваться для получения индекса неиспользуемого слота TLS ; индекс слота TLS тогда будет считаться «использованным».

The TlsGetValue и TlsSetValue Затем функции используются для чтения и записи адреса памяти в локальную переменную потока, идентифицируемую индексом слота TLS . TlsSetValue влияет только на переменную текущего потока. TlsFree Функция может быть вызвана для освобождения индекса слота TLS .

Для каждого потока существует блок информации о потоке Win32 . Одна из записей в этом блоке — это таблица локального хранилища для этого потока. [1] TlsAlloc возвращает индекс этой таблицы, уникальный для каждого адресного пространства, для каждого вызова. Каждый поток имеет собственную копию таблицы локального хранилища потока. Следовательно, каждый поток может независимо использовать TlsSetValue(index) и получать указанное значение через TlsGetValue(index), поскольку они устанавливают и ищут запись в собственной таблице потока.

Помимо семейства функций TlsXxx, исполняемые файлы Windows могут определять раздел, который отображается на другую страницу для каждого потока исполняемого процесса. В отличие от значений TlsXxx, эти страницы могут содержать произвольные и действительные адреса. Однако эти адреса различны для каждого исполняемого потока и поэтому не должны передаваться асинхронным функциям (которые могут выполняться в другом потоке) или иным образом передаваться в код, который предполагает, что виртуальный адрес уникален в пределах всего процесса. Разделы TLS управляются с помощью подкачки памяти , а их размер квантуется до размера страницы (4 КБ на машинах x86). Такие разделы могут быть определены только внутри основного исполняемого файла программы — библиотеки DLL не должны содержать такие разделы, поскольку они неправильно инициализируются при загрузке с помощью LoadLibrary.

Реализация Pthreads

[ редактировать ]

В API Pthreads локальная для потока память обозначается термином «данные, специфичные для потока».

Функции pthread_key_create и pthread_key_delete используются соответственно для создания и удаления ключа для данных, специфичных для потока. Тип ключа явно оставлен непрозрачным и называется pthread_key_t. Этот ключ виден всем потокам. В каждом потоке ключ может быть связан с данными, специфичными для потока, через pthread_setspecific. Данные можно позже получить с помощью pthread_getspecific.

Кроме того pthread_key_create при необходимости может принять функцию деструктора, которая будет автоматически вызываться при выходе из потока, если данные, специфичные для потока, не равны NULL . Деструктор получает значение, связанное с ключом, в качестве параметра, чтобы он мог выполнять действия по очистке (закрытие соединений, освобождение памяти и т. д.). Даже если указан деструктор, программа все равно должна вызывать pthread_key_delete чтобы освободить данные, специфичные для потока, на уровне процесса (деструктор освобождает только данные, локальные для потока).

Реализация для конкретного языка

[ редактировать ]

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

В C11 ключевое слово _Thread_local используется для определения локальных переменных потока. Заголовок <threads.h>, если поддерживается, определяет thread_local как синоним этого ключевого слова. Пример использования:

#include <threads.h>
thread_local int foo = 0;

И С11, <threads.h> также определяет ряд функций для получения, изменения и уничтожения локального хранилища потока, используя имена, начинающиеся с tss_. В С23, thread_local само становится ключевым словом. [2]

C++11 представляет thread_local[3] ключевое слово, которое можно использовать в следующих случаях

  • Переменные уровня пространства имен (глобальные)
  • Статические переменные файла
  • Статические переменные функции
  • Статические переменные-члены

Помимо этого, различные реализации компилятора предоставляют особые способы объявления локальных переменных потока:

В версиях Windows до Vista и Server 2008: __declspec(thread) работает в DLL только тогда, когда эти DLL привязаны к исполняемому файлу, и не будет работать для тех, которые загружены с помощью LoadLibrary() (может произойти сбой защиты или повреждение данных). [10]

Общий Лисп и другие диалекты

[ редактировать ]

Common Lisp предоставляет функцию, называемую переменными с динамической областью действия .

Динамические переменные имеют привязку, которая является частной для вызова функции и всех дочерних элементов, вызываемых этой функцией.

Эта абстракция естественным образом отображается в хранилище, специфичное для потоков, и реализации Lisp, предоставляющие потоки, делают это. Common Lisp имеет множество стандартных динамических переменных, и поэтому потоки не могут быть разумно добавлены в реализацию языка, если эти переменные не имеют локальной семантики потока в динамическом связывании.

Например, стандартная переменная *print-base* определяет систему счисления по умолчанию, в которой печатаются целые числа. Если эта переменная переопределена, то весь включающий код будет печатать целые числа в альтернативной системе счисления:

;;; function foo and its children will print
;; in hexadecimal:
(let ((*print-base* 16)) (foo))

Если функции могут выполняться одновременно в разных потоках, эта привязка должна быть должным образом локальной для потока, иначе каждый поток будет бороться за то, кто контролирует глобальную систему счисления печати.

В версии D 2 все статические и глобальные переменные по умолчанию являются локальными для потока и объявляются с синтаксисом, похожим на «обычные» глобальные и статические переменные в других языках. Глобальные переменные должны быть явно запрошены с использованием общего ключевого слова:

int threadLocal;  // This is a thread-local variable.
shared int global;  // This is a global variable shared with all threads.

Ключевое слово common работает и как класс хранения, и как квалификатор типа на общие переменные распространяются некоторые ограничения, которые статически обеспечивают целостность данных. [13] Чтобы объявить «классическую» глобальную переменную без этих ограничений, unsafe __gshared : необходимо использовать ключевое слово [14]

__gshared int global;  // This is a plain old global variable.

В Java локальные переменные потока реализуются с помощью ThreadLocal объект класса . [15] ThreadLocal содержит переменную типа T, [15] который доступен через методы get/set. Например, переменная ThreadLocal, содержащая целочисленное значение, выглядит следующим образом:

private static final ThreadLocal<Integer> myThreadLocalInteger = new ThreadLocal<Integer>();

По крайней мере, для Oracle/OpenJDK здесь не используется собственное локальное хранилище потоков, несмотря на то, что потоки ОС используются для других аспектов потоковой обработки Java. Вместо этого каждый объект Thread хранит (не потокобезопасную) карту объектов ThreadLocal с их значениями (в отличие от того, что каждый объект ThreadLocal имеет карту объектов Thread со значениями и несет накладные расходы на производительность). [16]

Языки .NET: C# и другие.

[ редактировать ]

В языках .NET Framework , таких как C# , статические поля могут быть помечены атрибутом ThreadStatic : [17] : 898 

class FooBar
{
    [ThreadStatic]
    private static int _foo;
}

В .NET Framework 4.0 System.Threading.ThreadLocal<T> для выделения и ленивой загрузки локальных переменных потока. доступен класс [17] : 899 

class FooBar
{
    private static System.Threading.ThreadLocal<int> _foo;
}

Также API для динамического выделения локальных переменных потока. доступен [17] : 899–890 

Объектный Паскаль

[ редактировать ]

В Object Pascal ( Delphi ) или Free Pascal зарезервированное ключевое слово threadvar можно использовать вместо 'var' для объявления переменных с использованием локального хранилища потока.

var
   mydata_process: integer;
threadvar
   mydata_threadlocal: integer;

В Cocoa , GNUstep и OpenStep каждый NSThread объект имеет локальный словарь потока, доступ к которому можно получить через threadDictionary метод.

NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
dict[@"A key"] = @"Some data";

Потоки в Perl были добавлены на позднем этапе развития языка, после того как большой объем существующего кода уже присутствовал в Комплексной сети архивов Perl (CPAN). Таким образом, потоки в Perl по умолчанию занимают собственное локальное хранилище для всех переменных, чтобы минимизировать влияние потоков на существующий код, не поддерживающий потоки. В Perl переменную, совместно используемую потоком, можно создать с помощью атрибута:

use threads;
use threads::shared;

my $localvar;
my $sharedvar :shared;

В PureBasic переменные потока объявляются с помощью ключевого слова Threaded .

Threaded Var

В Python версии 2.4 или более поздней версии локальный класс в модуле потоков можно использовать для создания локального хранилища потока.

import threading
mydata = threading.local()
mydata.x = 1

Можно создать несколько экземпляров локального класса для хранения разных наборов переменных. [18] Таким образом, это не синглтон .

Ruby может создавать локальные переменные потока и получать к ним доступ, используя []=/ [] методы:

Thread.current[:user_id] = 1

Ржавчина

[ редактировать ]

Локальные переменные потока могут быть созданы в Rust с помощью thread_local! макрос, предоставляемый стандартной библиотекой Rust:

use std::cell::RefCell;
use std::thread;

thread_local!(static FOO: RefCell<u32> = RefCell::new(1));

FOO.with(|f| {
    assert_eq!(*f.borrow(), 1);
    *f.borrow_mut() = 2;
});

// each thread starts out with the initial value of 1, even though this thread already changed its copy of the thread local value to 2
let t = thread::spawn(move || {
    FOO.with(|f| {
        assert_eq!(*f.borrow(), 1);
        *f.borrow_mut() = 3;
    });
});

// wait for the thread to complete and bail out on panic
t.join().unwrap();

// original thread retains the original value of 2 despite the child thread changing the value to 3 for that thread
FOO.with(|f| {
    assert_eq!(*f.borrow(), 2);
});

См. также

[ редактировать ]
  • OpenMP — еще одно средство многопроцессорной обработки с общей памятью, которое поддерживает попоточное хранение с помощью «предложений атрибутов совместного использования данных» (см. §Пункты ).
  1. ^ Пьетрек, Мэтт (май 2006 г.). «Под капотом» . MSDN . Проверено 6 апреля 2010 г.
  2. ^ «Библиотека поддержки параллелизма — cppreference.com» . ru.cppreference.com .
  3. ^ Раздел 3.7.2 в стандарте C++11.
  4. ^ «Информация о компиляторе C, относящаяся к реализации Sun» . Руководство пользователя C Sun Studio 8 . 2004. 2.3 Спецификатор локального хранилища потока.
  5. ^ «Компиляторы XL C/C++» . Август 2010 г. Локальное хранилище потоков (TLS). Архивировано из оригинала 11 апреля 2011 года.
  6. ^ «Локальное хранилище потока» . GCC 3.3.1 Руководство . 2003.
  7. ^ «Примечания к выпуску LLVM 2.0» . 23 мая 2007 г. Улучшения llvm-gcc.
  8. ^ «Расширения языка Clang — документация Clang 3.8» . Введение. В этом документе описаны языковые расширения, предоставляемые Clang. В дополнение к перечисленным здесь языковым расширениям Clang стремится поддерживать широкий спектр расширений GCC. Дополнительную информацию об этих расширениях см. в руководстве GCC.
  9. ^ «Компилятор Intel® C++ 8.1 для Linux: примечания к выпуску для процессоров Intel IA-32 и Itanium®» (PDF) . 2004. Локальное хранилище потоков. Архивировано из оригинала (PDF) 19 января 2015 года.
  10. Перейти обратно: Перейти обратно: а б Визуальная Студия 2003: «Локальное хранилище потоков (TLS)» . Документы Майкрософт . 5 июня 2017 г.
  11. ^ Компилятор Intel C++ 10.0 (Windows): локальное хранилище потоков
  12. ^ «Атрибуты в Clang — документация Clang 3.8» . нить.
  13. ^ Александреску, Андрей (6 июля 2010 г.). Глава 13. Параллелизм . Сообщите ИТ-специалистам. п. 3 . Проверено 3 января 2014 г. {{cite book}}: |website= игнорируется ( помогите )
  14. ^ Брайт, Уолтер (12 мая 2009 г.). «Миграция на общий доступ» . dlang.org . Проверено 3 января 2014 г.
  15. Перейти обратно: Перейти обратно: а б Блох 2018 , с. 151-155, §пункт 33. Рассмотрите типобезопасные гетерогенные контейнеры.
  16. ^ «Как реализован Java ThreadLocal?» . Переполнение стека . Обмен стеками . Проверено 27 декабря 2015 г.
  17. Перейти обратно: Перейти обратно: а б с Альбахари 2022 .
  18. ^ «cpython/Lib/_threading_local.py в версии 3.12 · python/cpython» . Гитхаб . Проверено 25 октября 2023 г.

Библиография

[ редактировать ]
  • Альбахари, Джозеф (2022). C# 10 в двух словах (первое изд.). О'Рейли. ISBN  978-1-098-12195-2 .
  • Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN  978-0134685991 .


[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a1e09c37d30af1f84499ed533b53ed63__1721112960
URL1:https://arc.ask3.ru/arc/aa/a1/63/a1e09c37d30af1f84499ed533b53ed63.html
Заголовок, (Title) документа по адресу, URL1:
Thread-local storage - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)