В C ++ являются ли статические инициализации примитивных типов постоянными значениями поточно-ориентированными? - PullRequest
10 голосов
/ 02 февраля 2010

То есть, ожидается ли следующее выполнение корректно даже в многопоточной среде?

int dostuff(void) {
    static int somevalue = 12345;
    return somevalue;
}

Или это может быть вызвано несколькими потоками, и один вызов может вернуть мусор, который был на &somevalue до начала выполнения?

Ответы [ 5 ]

10 голосов
/ 02 февраля 2010

Раздел 6.7 стандарта гласит:

Инициализация нуля всех локальных объекты со статической продолжительностью хранения выполняется перед любым другим инициализация происходит. Местный объект типа POD со статическим хранилищем продолжительность инициализируется с константа-выражения инициализируется прежде чем его блок впервые введен. реализация разрешено выполнять ранняя инициализация других локальных объекты со статической продолжительностью хранения при тех же условиях, что реализация разрешена статически инициализировать объект с статическая продолжительность хранения в пространстве имен объем. В противном случае такой объект инициализировал первый контроль времени проходит через свою декларацию; такие объект считается инициализированным по завершении его инициализация. Если инициализация выходит, выбрасывая исключение, инициализация не завершена, поэтому будет повторен в следующий раз контроль входит в декларацию. Если контроль повторно вводит декларацию (рекурсивно) в то время как объект будучи инициализированным, поведение не определено.

Так что, если это POD-тип, то, похоже, инициализация происходит при запуске до запуска новых потоков. Для не POD-типов это более сложно, стандарт говорит, что поведение не определено (если где-то еще не сказано что-то о безопасности потока во время инициализации).

Мне известно, что при инициализации не POD-объекта GCC захватывает мьютекс, чтобы предотвратить его инициализацию дважды (я знаю это, потому что однажды заблокировал программу, случайно рекурсивно инициализируя статический объект).

К сожалению, я не могу сказать вам, относится ли это к другим компиляторам или это требуется в других местах стандарта.

4 голосов
/ 02 февраля 2010

Да, это абсолютно безопасно (на большинстве компиляторов). Я бы порекомендовал добавить точку останова и посмотреть, как выполняется присвоение вашего конкретного компилятора. Я не могу сказать вам, сколько раз "стандарты" нарушаются.

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

На g ++ для OS X 10.6.2 это машинный код, сгенерированный для вашей функции:

push   rbp
mov    rbp,rsp
lea    rax,[rip+0x2067]        # 0x100003170 <_ZZ7dostuffvE9somevalue>
mov    eax,DWORD PTR [rax]
leave  
ret

Как видите, назначения нет. Компилятор запек примитив во время сборки.

2 голосов
/ 02 февраля 2010

Из стандарта C ++, раздел 6.7:

Локальный объект типа POD (3.9) со статической продолжительностью хранения инициализируется с помощью константных выражений инициализируется до того, как его блок впервые вошел.

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

2 голосов
/ 02 февраля 2010

Поскольку для инициализатора somevalue не требуется вызов конструктора, это будет работать нормально (somevalue будет инициализировано во время сборки).

Теперь, если вы инициализировали значение, для которого требовался конструктор:

void whatever()
{
    static std::string value("bad");

    ...
}

Тогда у вас могут возникнуть проблемы с несколькими потоками. Внутренне это превратится в нечто вроде:

void whatever()
{
    static bool value_initialized = false;
    static string_struct value;

    if (!initialized)
    {
        construct_string(&value, "bad");
        value_initialized = false;
    }

    ....
 }

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

0 голосов
/ 02 февраля 2010

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

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

Обратите внимание, что это, похоже, зависит от версий компилятора (что вы ожидаете, учитывая, что мы идем в «неопределенных» областях поведения)

...