Неустойчиво читает и пишет атомарно на Windows + VisualC?

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

Теперь стандартный стандартный ответ C (++), очевидно, не.

Однако в компиляторе Windows и Visual С++ ситуация кажется не столь ясной.

Я недавно ответил и процитировал официальные документы MSDN на volatile

Спецификация Microsoft

Объекты, объявленные как изменчивые, являются (...)

  • Запись во volatile object (volatile write) имеет семантику Release; ссылка на глобальный или статический объект ? который происходит до записи в волатильный объект в последовательности команд будет происходить до этого volatile записывает в скомпилированный двоичный файл.
  • Чтение изменчивого объекта (volatile read) имеет семантику Acquire; ссылка на глобальный или статический объект ? который возникает после чтения энергозависимой памяти в инструкция последовательность будет происходить после этого волатильного чтения в скомпилированном двоичном файле.

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

[акцент мой]

Теперь, прочитав это, мне показалось, что переменная volatile будет обрабатываться компилятором MS, поскольку std::atomic будет в следующем стандарте С++ 11.

Однако в комментарии к моему ответу пользователь Hans Passant написал: "Эта статья MSDN очень Несчастливо, это неверно. Вы не можете реализовать блокировку с изменчивым, даже с версией Microsoft. (...)"


Обратите внимание: пример, указанный в MSDN, кажется довольно подозрительным, поскольку вы не можете вообще реализовать блокировку без атомного обмена. (А также указал Алекс.) Это все еще оставляет вопрос по адресу. к действительности другой информации, указанной в этой статье MSDN, особенно для случаев использования, таких как здесь и здесь. )


Кроме того, существуют документы для функций "Блокировка", особенно InterlockedExchange, с помощью volatile (!?) и выполняет атомарное чтение + запись. (Обратите внимание, что один вопрос, который у нас есть на SO - Когда следует использовать InterlockedExchange? - не отвечает на вопрос, нужна ли эта функция для чтения или записи, только атомный доступ.)

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

Вернуться к вопросу

В Windows с Visual С++ (2005 - 2010) будет объявлена ​​переменная (32bit? int?) как volatile разрешить атомные чтения и записи этой переменной - или нет?

Что особенно важно для меня, так это то, что это должно быть (или нет) на Windows/VС++ независимо от процессора или платформы, на которой работает программа. (То есть, имеет ли значение, работает ли WinXP/32bit или Windows 2008R2/64bit на Itanum2?)

Пожалуйста, создайте резервную копию своего ответа с достоверной информацией, ссылками, тестовыми примерами!

Ответ 1

Да, они атомарны в windows/vС++ (Предполагая, что вы выполняете требования к выравниванию и т.д. или курс)

Однако для блокировки вам понадобится атомный тест и установите, или сравните и замените instuction или подобное, а не просто атомное обновление или прочитайте.

В противном случае невозможно протестировать блокировку и потребовать ее в одной неделимой операции.

РЕДАКТИРОВАТЬ: Как указано ниже, все выровненные обращения к памяти на x86 из 32 бит или ниже в любом случае являются атомарными. Ключевым моментом является то, что изменчивость позволяет упорядочить доступ к памяти. (Спасибо, что указали это в комментариях)

Ответ 2

Как и в Visual С++ 2005, летучие переменные являются атомарными. Но это относится только к этому конкретному классу компиляторов и к платформам x86/AMD64. Например, PowerPC может изменять порядок чтения/записи в памяти и требовать барьеров чтения/записи. Я не знаю, что такое семантика для компиляторов gcc-класса, но в любом случае использование volatile для атомистики не очень переносимо.

см. первое замечание "Специфика Microsoft": http://msdn.microsoft.com/en-us/library/12a04hfd%28VS.80%29.aspx

Ответ 3

Немного не по теме, но пусть все равно.

... есть документы для функций Interlocked *, особенно InterlockedExchange, который принимает изменчивую (!) переменную...

Если вы думаете об этом:

void foo(int volatile*);

Говорит ли он:

  • аргумент должен быть указателем на volatile int или
  • аргумент может также быть указателем на volatile int?

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

Следовательно, тот факт, что InterlockedExchangeX() имеет свой аргумент volatile-qualty, не означает, что он должен работать только с нестабильными целыми числами.

Ответ 4

Точка, вероятно, должна содержать такие вещи, как

singleton& get_instance()
{
    static volatile singleton* instance;
    static mutex instance_mutex;

    if (!instance)
    {
        raii_lock lock(instance_mutex);

        if (!instance) instance = new singleton;
    }

    return *instance;
}

который сломался бы, если бы instance было записано до завершения инициализации. С семантикой MSVC вам гарантируется, что, как только вы увидите instance != 0, объект завершит инициализацию (что не имеет места без правильной разделительной семантики, даже с традиционной изменчивой семантикой).

Этот шаблон с двойной проверкой (анти-) довольно распространен и сломан, если вы не предоставляете барьерную семантику. Однако, если есть гарантии, что доступ к volatile переменным приобретает + барьеры выпуска, то он работает.

Не полагайтесь на такую ​​настраиваемую семантику volatile. Я подозреваю, что это было введено, чтобы не сломать существующие кодовые базы. В любом случае не записывайте блокировки в соответствии с примером MSDN. Вероятно, это не работает (я сомневаюсь, что вы можете написать блокировку, используя только барьер: для этого вам нужны атомарные операции - CAS, TAS и т.д.).

Единственный переносимый способ записи шаблона блокировки с двойным проверкой - использовать С++ 0x, который обеспечивает подходящую модель памяти и явные барьеры.

Ответ 5

под x86, эти операции гарантированно будут атомарными без необходимости в инструкциях на основе LOCK, таких как Interlocked* (см. руководства для разработчиков Intel 3A, раздел 8.1):

Операции с базой памяти

всегда будут выполняться атомарно:

• Чтение или запись байта

• Чтение или запись слова, выровненного на 16-разрядная граница

• Чтение или запись двойного слова, выровненного на 32-битной границе

Процессор Pentium (и более новые процессоры с тех пор) гарантирует что всегда будут выполняться следующие дополнительные операции с памятью из атомарно:

• Чтение или запись квад-слова, выровненного на 64-битном граница

• 16-разрядный доступ к нераспакованным ячейкам памяти, которые подходят в 32-битной шине данных

Процессоры семейства P6 (и более новые процессоры, поскольку) гарантируют, что следующая дополнительная память операция всегда будет выполняться атомарно:

• Unaligned 16-, 32-, и 64-битный доступ к кэшированной памяти, которая вписывается в строку кэша

Это означает, что volatile будет использоваться только для предотвращения кэширования и переупорядочения команд компилятором (MSVC не будет вызывать атомарные операции для переменных переменных, они должны быть явно использованы).