Являются ли энергозависимые чтения и записи атомарными в Windows + VisualC? - PullRequest
10 голосов
/ 10 августа 2011

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

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

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

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

Microsoft Specific

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

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

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

[выделение мин]

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

Однако,в комментарии к моему ответу , пользователь Ханс Пассант писал "Эта статья MSDN очень неудачная, она совершенно неверна. Вы не можете реализовать блокировку с помощью volatile, недаже с версией Microsoft. (...) "


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


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

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

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

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

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

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

Ответы [ 5 ]

6 голосов
/ 10 августа 2011

Да, они являются атомарными в Windows / vc ++ (при условии, что вы соответствуете требованиям выравнивания и т. Д. Или курсам)

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

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

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

3 голосов
/ 10 августа 2011

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

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

1 голос
/ 10 августа 2011

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

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

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

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

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

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

• Чтение или запись четырех слов, выровненных по 64-битовая граница

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

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

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

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

1 голос
/ 10 августа 2011

Смысл, вероятно, состоит в том, чтобы разрешить такие вещи, как

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 и т. Д.).

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

1 голос
/ 10 августа 2011

Немного не по теме, но все равно поехали.

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

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

void foo(int volatile*);

Это говорит:

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

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

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

...