2010-02-26 2 views
6

Я пишу приложение на C++.переменные с переменным переменным C++

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

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

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

Спасибо!

+9

Что заставляет вас думать, что записи будут атомарными? – bmargulies

+4

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

+0

Я делал такие вещи на встроенном одноядерном процессоре PowerPC и работает надежно. Лучше не делать read-modify-write (например, '++ sharedInt'), если возможно, что два потока имеют доступ на запись. (На самом деле, если два потока могут писать, это, вероятно, полезно только в том случае, если вы ограничиваете **, когда ** они могут писать. Например, потоку A разрешено изменять 'sharedInt' от 0 до 1, тогда как потоку B разрешено изменять его с 1 до 0.) – Dan

ответ

3

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

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

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

+0

Это НЕправда, когда вы строите приложение/алгоритмы HPC, вы, как правило, хорошо знаете архитектуру, с которой собираетесь работать. И таким образом вы не собираетесь добавлять бесполезный замок, если вам это не нужно. – Ben

+0

Ну гул, по крайней мере, моя точка зрения. – Ben

+0

Я всегда думал, что запись int была атомарным op (по крайней мере, на процессоре x86). У вас есть хорошая документация об атомных операциях? –

0

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

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

13

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

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

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

+0

«На практике волатильность не делает то, что вы имеете в виду». Какие?? Volatile предназначен для предотвращения оптимизации компилятора для переменной. Это очень, очень, очень рекомендуется для многопоточного программирования. Не пытайтесь многопоточно программировать без изменчивости. Ваша программа может работать отлично в течение многих лет и внезапно вылететь из ниоткуда. – 2016-01-28 14:48:55

+0

@annoying_squid, см. Http://stackoverflow.com/a/2485177/136208. Начиная с C++ 11, 'std :: atomic <>', вероятно, является способом получить то, что вы, хотя volatile будет предоставлять. – AProgrammer

-4

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

EDIT (в основном о PM100 комментариях): Я понимаю, что мои убеждения не ссылка, так что я нашел что-то процитировать :)

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

От Dr Dobb's

Больше интересный:

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

из Искусства многопроцессорного программирования Морис Herlihy & Nir Shavit

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

+0

Почему этот ответ подавлен? – anon

+1

потому что это неправильно. volatile не имеет ничего общего с кэшами памяти. – pm100

+0

@ pm100, Это не было предназначено для этого, я согласен, но это связано с кешами, см. Мое редактирование, пожалуйста. – Ben

4

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

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

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

+2

Летучий не является абсолютным минимумом. Это значительно ниже минимума. Минимум также должен был бы предотвратить переупорядочение чтения/записи вокруг общей переменной. – jalf

+0

@jalf - сколько градаций ниже «абсолютного минимума», вы хотите рассмотреть? –

+0

Только один: «ниже этого»;) – jalf

1

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

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

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

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

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

Если p является указателем на порт ввода/вывода отображаемой памяти, вы хотели бы первый вариант, где порт проверяется до ввода петли e очень время.

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

+6

Речь идет не только о чтении: «for (i = 0; i <10; ++ i) {j = i;} можно заменить на j = 10; неустойчивый. – stefaanv

Смежные вопросы