Переменные переменные переменные С++

Я пишу приложение на С++.

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

В С++ все, что может быть изменено без компилятора, "реализующего", что он изменяется, должен быть помечен как volatile right? Так что, если мой код многопоточен, и один поток может писать в var, а другой читает из него, мне нужно отметить var volaltile?

[У меня нет условия гонки, так как я полагаюсь на запись в ints атомарным]

Спасибо!

Ответ 1

У С++ еще нет никаких условий для многопоточности. На практике volatile не делает то, что вы имеете в виду (он был разработан для аппаратного обеспечения с адресами памяти, а в то время как две проблемы подобны, они достаточно различны, что волатильность не делает правильные вещи - обратите внимание, что volatile был использован в других язык для использования в контекстах mt).

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

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

Ответ 2

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

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

Как только вы позаботитесь о таких деталях, вы, в конце концов, повторно изобрели переменную состояния (aka event), которая вряд ли будет быстрее, чем та, которая предоставляется библиотекой потоков. Или же проверено. Не придумывайте свои собственные, потоки достаточно сложны, чтобы получить право, вам не нужно FUD, чтобы не быть уверенным в том, что основные примитивы прочны.

Ответ 3

volatile инструктирует компилятор не оптимизировать "интуицию" значения переменной или использования, поскольку она может быть оптимизирована "снаружи".

volatile не обеспечит никакой синхронизации, и ваше предположение о том, что запись в int является атомарной, почти реалистично!

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

Ответ 4

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

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

while (*p)
{
  // ...
}

В приведенном выше коде, если *p не записано внутри цикла, компилятор может решить перенести чтение за пределы цикла, как это:

cached_p=*p
while (cached_p)
{
  // ...
}

Если p является указателем на порт ввода-вывода с отображением памяти, вам нужна первая версия, где порт проверяется до того, как цикл будет введен каждый раз.

Если p является указателем на память в многопоточном приложении, вам все равно не гарантируется, что записи являются атомарными.

Ответ 5

Без блокировки вы все равно можете получить "невозможные" повторные заказы, сделанные компилятором или процессором. И нет никакой гарантии, что записи в ints являются атомарными.

Лучше использовать правильную блокировку.

Ответ 6

Volatile решит вашу проблему, т.е. это гарантирует согласованность между всеми кэшами системы. Однако это будет неэффективно, поскольку он обновит переменную в памяти для каждого доступа R или W. Вместо этого вы можете использовать индикатор памяти, только тогда, когда это необходимо. Если вы работаете с gcc/icc, посмотрите на встроенные функции синхронизации: http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

РЕДАКТИРОВАТЬ (в основном о комментарии pm100): Я понимаю, что мои убеждения не являются ссылкой, поэтому я нашел что-то, чтобы процитировать:)

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

Из Dr Dobb's

Более интересно:

Неустойчивые поля линеаризуемы. Чтение изменчивого поля похоже на приобретение блокировки; рабочая память недействительна, и текущее значение изменчивого поля перечитывается из памяти. Написание изменчивого поля похоже на освобождение блокировки: энергозависимое поле немедленно записывается обратно в память. (это все о согласованности, а не об атомарности)

из проекта многопроцессорного программирования, Морис Херлихи и Нир Шавит

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