В C++ статические инициализации типов примитивов к ориентированным на многопотоковое исполнение постоянным величинам?

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

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

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

10
задан Jonathan Grynspan 1 February 2010 в 22:20
поделиться

5 ответов

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

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

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

2
ответ дан 3 December 2019 в 20:41
поделиться

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

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

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

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

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

4
ответ дан 3 December 2019 в 20:41
поделиться

правовая оговорка: я говорю в терминах Java здесь

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

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

проблема, когда вы начнете иметь одиночный элемент, необходимо будет знать каждый вшивые песчаные детали того, что вы делаете, потому что вы делаете партию строгого предположения на вашем singleton-классе, самый важный, что это будет единственный один класс в системе. затем вы начинаете использовать его, предполагая, что это всегда будет однократная точка к вашему ресурсу, и затем противная ошибка возникает, потому что ваш класс был теперь развернут на ejb сервере, и каждый ejb контекст имеет свой собственный одиночный элемент плюс еще один одиночный элемент для каждого jsp, который был перезагружен с сервера плюс один одиночный элемент в течение каждого раза, когда ваш одиночный элемент был сериализирован и десериализован (как вы, вероятно, имеете, забыл переопределять readResolve () метод).

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

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

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

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

прокси предназначены для того, чтобы охватить роль «предусматривать ресурса по всей системе», будь то одно приложение, клиентская серверная система, различные компоненты одной и той же системы и так далее, с тем дополнительным преимуществом, что при использовании метода извлечения ресурса является непрозрачным. вы можете иметь их предоставить вам singleton, содержащий хэшмап кэшированных значений, вы можете иметь доступ к memcached somhere в сети, вы можете иметь их чтение csv во время тестов, все без изменения, как вы вызываете их из приложения.

-121--3367052-

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

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

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

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

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

10
ответ дан 3 December 2019 в 20:41
поделиться

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

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

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;
    }

    ....
 }

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

2
ответ дан 3 December 2019 в 20:41
поделиться

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

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

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

.
0
ответ дан 3 December 2019 в 20:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: