Неустойчиво читает и пишет атомарно на 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 не будет вызывать атомарные операции для переменных переменных, они должны быть явно использованы).