С ++ volatile требуется при вращении с помощью оператора boost :: shared_ptr bool ()?

Возможный дубликат:
Когда использовать volatile с многопоточность?

У меня есть два потока, ссылающихся на один и тот же boost::shared_ptr:

boost::shared_ptr<Widget> shared;

В потоке вращается, ожидая, пока другой поток сбросит boost::shared_ptr:

while(shared)
   boost::thread::yield();

И в какой-то момент другой поток вызовет:

shared.reset();

Мой вопрос в том, нужно ли мне объявлять общий указатель как volatile, чтобы компилятор не оптимизировал вызов shared.operator bool() вне цикла и никогда не обнаруживал изменения? Я знаю, что если бы я просто перебирал переменную в цикле, ожидая, пока она достигнет 0, мне понадобится volatile, но я не уверен, реализован ли boost::shared_ptr таким образом, что в этом нет необходимости.

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


person JaredC    schedule 11.01.2011    source источник
comment
О, парень. Вот так...   -  person John Dibling    schedule 11.01.2011
comment
Пожалуйста, не надо. Прекрати это сейчас. Есть огромное количество причин не делать то, что вы пытаетесь делать. Затем внимательно прочитайте boost.org/doc/libs/1_45_0/doc/html/thread/   -  person Juliano    schedule 11.01.2011
comment
@JaredC: Пожалуйста, взгляните на [this] [1], [this] [2] и [this] [3]] для получения дополнительной информации о volatile [1]: stackoverflow.com/questions/4168735/ [2]: software.intel.com / en-us / blogs / 2007/11/30 / [3]: stackoverflow.com/questions/4136900/   -  person John Dibling    schedule 11.01.2011
comment
@JaredC: И это, наверное, прежде всего: stackoverflow.com/questions/4557979/   -  person John Dibling    schedule 12.01.2011
comment
переменная состояния - накладные расходы, вы это измерили? Похоже на преждевременную оптимизацию.   -  person Yakov Galka    schedule 12.01.2011
comment
@John: На самом деле ответ на вопрос о последней ссылке (volatile бесполезен), похоже, тоже отвечает на этот вопрос.   -  person sbi    schedule 12.01.2011
comment
@ybung Мы находимся в процессе снятия блокировок с этого участка кода, чувствительного к производительности, поэтому мы определили их накладные расходы. Этот код выполняется более 10000 раз в секунду, поэтому мы пытаемся его оптимизировать.   -  person JaredC    schedule 12.01.2011
comment
@JaredC: Учитывая вашу мотивацию, помните, что истинная цель volatile - подавить оптимизацию. Кажется, это полная противоположность той причине, по которой вы, возможно, пытаетесь его использовать.   -  person John Dibling    schedule 12.01.2011
comment
@John: Спасибо за ссылки и вклад. Я собираюсь просмотреть эти статьи (и их ссылки (и их ссылки)), чтобы полностью понять volatile. Извините, что пишу на такую ​​наболевшую тему.   -  person JaredC    schedule 12.01.2011
comment
@JaredC: Не извиняйся. Это просто моя личная мыльница. Это отличный вопрос. Фактически, ваш вопрос побудил нас (в чате C ++) написать FAQ по этой теме.   -  person John Dibling    schedule 12.01.2011


Ответы (2)


Рентген 1:

Этот код, вероятно, не будет делать то, что вы думаете. Когда вы пишете такой код, вы вводите в свой код гонку данных. Это почти наверняка ошибка, которая приведет к недетерминированному сбою вашей программы.

Структуры данных (включая shared_ptr) обычно не предназначены для одновременного доступа. Не изменяйте одну и ту же структуру одновременно в более чем одном потоке. Это могло повредить структуру. Не изменяйте его в одном потоке и не читайте в другом потоке. Читатель мог видеть противоречивые данные. Вероятно несколько потоков могут читать его одновременно.

Если вы думаете, что действительно хотите сделать что-то из вышеперечисленного, узнайте, допускает ли структура данных некоторые из этих вариантов поведения, в разделе, вероятно, озаглавленном «Безопасность потоков». Если это позволяет им, еще раз посмотрите, действительно ли это нужно для вашей производительности, и затем используйте это. (Документация по shared_ptr НЕ разрешает то, что вы делаете.)

Рентген 2:

Теперь, что касается более высокого уровня, вам, вероятно, не следует выполнять синхронизацию потоков, ожидая, пока указатель не будет установлен в NULL. На самом деле, смотрите на условные переменные, барьеры или фьючерсы как на способ заставить один поток ждать, пока другой с чем-то не закончит. Это более приятный интерфейс, и кто бы ни посмотрел на ваш код в следующий раз (включая вас через 6 месяцев), он будет вам благодарен.

Я знаю, что вас беспокоит стоимость реальной синхронизации. Не беспокойся об этом. Все будет хорошо. Если вас беспокоит конкуренция за блокировку, используйте барьеры или фьючерсы, которые не требуют большой разделяемой блокировки.

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

Трек 3:

Надеюсь, что shared в вашем примере глобален. В противном случае у вас есть несколько потоков с локальными ссылками на один и тот же shared_ptr, который указывает на реальный объект, который вам интересен. Это своего рода поражение цели наличия указателя с подсчетом ссылок. Скажите, пожалуйста, это глобально.

person Karmastan    schedule 11.01.2011
comment
Rant3: shared - это глобальная переменная, да. Rant2: Мы находимся на этапе оптимизации, поэтому я в первую очередь снимаю блокировки в этом разделе кода. Rant1: Это самый веский аргумент, который я слышал, чтобы этого не делать, спасибо. - person JaredC; 12.01.2011

На самом деле вам следует использовать переменные условия. Ожидание в ожидании - это зло.

Изменить: также в зависимости от вашей задачи Futures может быть еще более чистым способом достичь желаемого.

person Yakov Galka    schedule 11.01.2011
comment
Обычно я согласен, но в этом случае цикл почти никогда не должен выполняться, поэтому мы решили, что цикл занятости лучше, чем борьба за блокировку. - person JaredC; 11.01.2011
comment
Тогда вы можете легко проверить один раз, прежде чем приобретать блокировку, тогда вы почти никогда не получите блокировку. Тем не менее, это не повод для долгого ожидания. Без блокировки вы не можете гарантировать, что общий доступ останется пустым после цикла. - person Yakov Galka; 11.01.2011
comment
@JaredC: Если цикл почти никогда не выполняется, то блокировка почти никогда не будет оспорена. - person James McNellis; 11.01.2011
comment
ДжаредК: Цикл занятости никогда (*) лучше, чем использование монитора, периода. (Примечание: (*) в некоторых случаях это может быть лучше, а именно в коде ядра во встроенных системах без батарейного питания, но, скорее всего, это не ваш случай). - person Juliano; 11.01.2011
comment
@ybung, проверка один раз перед зацикливанием по-прежнему требует, чтобы поток, вызывающий reset (), всегда получал блокировку. - person JaredC; 12.01.2011
comment
@JaredC: нет. вы всегда вызываете condVar.notify_all () без блокировки. - person Yakov Galka; 12.01.2011
comment
@ybung, но тогда есть условие гонки, при котором ожидающий поток, возможно, на самом деле не ждет ЕЩЕ, но уже увидел свое состояние как ложное. Затем происходит вызов reset (), и notify_all () точно никого не разбудит. Я что-то упускаю? - person JaredC; 12.01.2011
comment
@JaredC: ой, моя ошибка. Ты прямо здесь. - person Yakov Galka; 12.01.2011