порядок инициализации thread_local против глобальных переменных - PullRequest
7 голосов
/ 23 марта 2020

C .h:

#include <iostream>

class C {
public:
  explicit C(int id) { std::cout<<"Initialized "<<id<<"\n"; }
};

1. cpp:

#include "C.h"

C global(1);

2. cpp:

#include "C.h"

thread_local C thread(2);

int main() {}

Мой вопрос is: Гарантируется ли, что global будет инициализирован до thread?

Стандарт C ++, насколько я понимаю, несколько расплывчат в этом вопросе. Он говорит (из C ++ 17 n4659 черновик):

[basi c .start.static] Stati c инициализация

Переменные с продолжительностью хранения stati c инициализируются в результате запуска программы. Переменные с продолжительностью хранения потока инициализируются как следствие выполнения потока.

Разумеется, «инициация программы» происходит до «выполнения потока», но поскольку оба эти выражения появляются в стандарте только в это место, я ищу советов от настоящих языковых адвокатов.

Ответы [ 2 ]

5 голосов
/ 23 марта 2020

Я собираюсь использовать рабочий проект C ++ 20, так как формулировка там немного чище, хотя ни одно из настоящих правил не изменилось.

Во-первых, thread_local ведет себя в основном как static насколько нелокально идет: [basi c .st c .thread] / 2 :

[Примечание: переменная с продолжительностью хранения потока равна инициализируется, как указано в [basi c .start.static], [basi c .start.dynamic] и [stmt.dcl], и, если оно создано, уничтожается при выходе из потока ([basi c .start .срок]). - конец заметки]

Да, это заметка. Но нелокальный объект, объявленный thread_local, в основном static, так что это имеет смысл.

Теперь ни global, ни thread не имеют постоянной инициализации - поэтому оба инициализируются с нуля, а затем им требуется go dynamici c инициализация. До [basi c .start.dynamic] !

Dynami c инициализация нелокальной переменной с stati c длительность хранения неупорядочена, если переменная является неявно или явно созданной специализацией, частично упорядоченной, если переменная является встроенной переменной, которая не является неявно или явно созданной специализацией, а в противном случае упорядочена.

Ни одна из наших переменных не является специализацией, ни одна из них не является встроенной. Таким образом, оба упорядочены .

Объявление D является упорядоченным по внешнему виду перед объявлением E, если

  • D появляется в та же единица перевода, что и E, или
  • единица перевода, содержащая E, имеет интерфейсную зависимость от единицы перевода, содержащей D,

в любом случае до E.

Наши объявления не упорядочены по внешнему виду относительно друг друга.

Dynami c Инициализация нелокальных переменных V и W с stati c длительность хранения упорядочена как следует:

Хорошо, подпункт 1:

Если V и W имеют упорядоченную инициализацию, а определение V упорядочено по внешнему виду до определения W, или если V имеет частично упорядоченную инициализацию, W не имеет неупорядоченной инициализации, и для каждого определения E из W существует такое определение D из V, что D упорядочивается по внешнему виду до E,

Doesn ' т применить. Это сложное условие, но оно не применяется.

В противном случае, если программа запускает поток, отличный от основного потока, до инициализации V или W, не указывается, в каких потоках инициализируется V и W встречаются; инициализация не выполняется, если они происходят в одном и том же потоке.

Нет, нет потоков.

В противном случае инициализация V и W выполняется неопределенным образом.

Там мы go. global и thread имеют неопределенную последовательность.

Обратите также внимание на то, что:

Определяется реализацией, выполняется ли инициализация динамической c нелокальной встроенной переменной со значением stati c длительностью до первого оператора main или откладывается.

и:

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

0 голосов
/ 23 марта 2020

Нет гарантии и не может быть никакой формы гарантии - по крайней мере, в настоящее время.

Представьте себе следующий случай, у вас есть другая не связанная static глобальная переменная Z, которая использует ваш thread_local переменная во время инициализации или, скажем, даже создает другой поток и использует его - все во время его инициализации.

Теперь просто так случается, что эта static глобальная переменная Z инициализируется до вашего состояния c global переменная global. Это означает, что переменную thread_local нужно было инициализировать до вашей глобальной переменной stati c.

Примечание: в настоящее время нет способа гарантировать, в каком порядке инициализируются глобальные переменные stati c - известная проблема C ++. Таким образом, если вы используете одну глобальную переменную внутри другой, это может привести или не привести к ошибке - технически UB. Не думайте, что это влияет на thread_local переменных каким-либо образом, поскольку их механизм инициализации, как правило, сильно отличается.

...