2010-02-01 1 views
10

т. Е. Можно ли ожидать, что следующее будет выполняться корректно даже в многопоточной среде?В C++, являются ли статические инициализации примитивных типов постоянными значениями потокобезопасными?

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

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

+0

статический соп не вариант? –

+0

constness не будет работать здесь, так как int будет модифицироваться позже (в то время как мьютекс, известный для существования). У меня было ощущение, что любой здравомыслящий компилятор будет по крайней мере нулевым инициализировать статические целые числа в области функций до начала запуска (в этом случае, это достаточно хорошо для меня.) Это то, что легко можно использовать. –

+1

Технически нет. Но gcc имеет явный патч для гарантии того, что он работает в многопоточной среде. –

ответ

2

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

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

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

+0

Аг, не мог найти это в стандарте. Аг снова! В этом же разделе сказано: «Нулевая инициализация (8.5) всех локальных объектов со статической продолжительностью хранения (3.7.1) выполняется до любой другой инициализации». Итак, если начальное значение равно 0, значение 0 должно (согласно стандарту) быть допустимым даже в многопоточной среде, да? –

+0

Это, безусловно, это говорит. – 2010-02-01 23:21:11

+0

Просто убедитесь, что я правильно прочитал. Благодарю. :) –

4

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

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

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

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

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

+0

+1 для asm, хотя это компилятор специфический :-) – Justicle

+0

Также, я хотел бы видеть ASM, если someValue действительно был изменен внутри функции - я думаю, что GCC сделал некоторую оптимизацию здесь. У меня есть ощущение, что полученный код будет больше похож: http://stackoverflow.com/questions/2180501/in-c-are-static-initializations-of-primitive-types-to-constant-values-thread-s/ 2180547 # 2180547 – Justicle

+0

Когда я увеличиваю значение, оно выглядит точно так же, как указано выше, за исключением ASM прироста. Мутирование значения знаменует гонку, но не из-за инициализации, которая по-прежнему отсутствует. – pestilence669

2

Поскольку инициализатор 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; 
    } 

    .... 
} 

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

10

Раздела 6.7 стандарта это сказать:

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

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

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

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

+0

+1 для цитирования стандарта – Justicle

+3

gcc введенный поток безопасной статики в линии 4.x. –

+1

«Локальный объект типа POD со статической продолжительностью хранения, инициализированный константными выражениями, инициализируется до того, как его блок будет введен первым». то есть не при запуске. – 2010-02-01 23:19:42

0

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

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

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

Смежные вопросы