Продолжительность локального хранилища потока - это термин, используемый для обозначения данных, которые кажутся глобальными или статическими (с точки зрения используемых функций), но на самом деле существует одна копия на поток.
Он добавляет к текущему автоматическому (существует во время блока / функции), статическому (существует на время выполнения программы) и динамическому (существует в куче между выделением и освобождением).
Что-то, что является локальным для потока, создается при создании потока и удаляется, когда поток останавливается.
Ниже приведены некоторые примеры.
Подумайте о генераторе случайных чисел, в котором начальное число должно поддерживаться для каждого потока. Использование начального числа локального потока означает, что каждый поток получает свою собственную последовательность случайных чисел, независимую от других потоков.
Если бы ваше семя было локальной переменной в случайной функции, оно было бы инициализировано каждый раз, когда вы его вызывали, давая вам каждый раз одно и то же число. Если бы он был глобальным, потоки вмешивались бы в последовательности друг друга.
Другой пример - это что-то вроде того, strtok
где состояние токенизации хранится в зависимости от потока. Таким образом, один поток может быть уверен, что другие потоки не испортят его усилия по токенизации, при этом сохраняя состояние при нескольких вызовах strtok
- это в основном делает strtok_r
(поточно-ориентированную версию) избыточной.
Оба этих примера допускают существование локальной переменной потока. в функции, которая ее использует. В предварительно запрограммированном коде это будет просто статическая переменная продолжительности хранения внутри функции. Для потоков это изменено на продолжительность локального хранилища потока.
Еще один пример был бы что-то вроде errno
. Вам не нужно изменять отдельные потоки errno
после сбоя одного из ваших вызовов, но до того, как вы сможете проверить переменную, и при этом вам нужна только одна копия для каждого потока.
На этом сайте есть разумное описание различных спецификаторов продолжительности хранения.
strtok
.strtok
не работает даже в однопоточной среде.r
означает «повторный вход», который не имеет ничего общего с безопасностью потоков. Верно, что вы можете заставить некоторые вещи работать поточно-безопасно с помощью локального хранилища потока, но вы не можете сделать их реентерабельными.strtok
вызова других функций.while (something) { char *next = strtok(whatever); someFunction(next); // someFunction calls strtok }
Когда вы объявляете переменную,
thread_local
каждый поток имеет свою собственную копию. Когда вы обращаетесь к нему по имени, используется копия, связанная с текущим потоком. напримерЭтот код выведет «2349», «3249», «4239», «4329», «2439» или «3429», но ничего больше. У каждого потока есть своя собственная копия
i
, которая назначается, увеличивается и затем печатается. Поток работаетmain
также есть собственная копия, которая назначается в начале, а затем остается неизменной. Эти копии полностью независимы и имеют разные адреса.В этом отношении особенным является только имя - если вы берете адрес
thread_local
переменной, то у вас просто есть обычный указатель на нормальный объект, который вы можете свободно передавать между потоками. напримерПоскольку адрес
i
передается функции потока, то копия,i
принадлежащая основному потоку, может быть назначена, даже если это такthread_local
. Таким образом, эта программа выведет «42». Если вы это сделаете, вам нужно позаботиться о том, чтобы к*p
ним не было доступа после выхода из потока, которому он принадлежит, иначе вы получите висящий указатель и неопределенное поведение, как и в любом другом случае, когда объект, на который указывает, уничтожается.thread_local
переменные инициализируются «перед первым использованием», поэтому, если данный поток никогда не касается их, то они не обязательно когда-либо инициализируются. Это позволяет компиляторам избегать создания каждойthread_local
переменной в программе для потока, который является полностью автономным и не затрагивает ни одну из них. напримерВ этой программе есть 2 потока: основной поток и поток, созданный вручную. Ни один из потоков не вызывает
f
, поэтомуthread_local
объект никогда не используется. Поэтому не определено, будет ли компилятор создавать 0, 1 или 2 экземпляраmy_class
, а вывод может быть «», «hellohellogoodbyegoodbye» или «hellogoodbye».источник
g()
вызов в началоthreadFunc
, то на выходе будет0304029
либо какая-то другая перестановка пар02
,03
и04
. То есть, несмотря на то, что 9 назначаетсяi
до создания потоков, потоки получают только что созданную копиюi
wherei=0
. Еслиi
присвоеноthread_local int i = random_integer()
, то каждый поток получает новое случайное целое число.02
,03
,04
, могут существовать и другие последовательности , как020043
Локальное хранилище потока во всех аспектах подобно статическому (= глобальному) хранилищу, только каждый поток имеет отдельную копию объекта. Время жизни объекта начинается либо при запуске потока (для глобальных переменных), либо при первой инициализации (для локальной статики блока) и заканчивается, когда поток заканчивается (то есть когда
join()
вызывается).Следовательно, только переменные, которые также могут быть объявлены,
static
могут быть объявлены какthread_local
, то есть глобальные переменные (точнее: переменные «в области пространства имен»), статические члены класса и статические переменные блока (в этом случаеstatic
подразумевается).В качестве примера предположим, что у вас есть пул потоков и вы хотите знать, насколько хорошо была сбалансирована ваша рабочая нагрузка:
Это выведет статистику использования потока, например, с такой реализацией:
источник