Является ли это правило об использовании volatile строгим?

Я видел эту фразу:

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

из этой статьи и этого предложения:

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

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


person Pooria    schedule 12.11.2010    source источник
comment
Сделал для вас форматирование, откатитесь, если не понравится   -  person John Dibling    schedule 12.11.2010
comment
@Pooria: Не совсем обман, но на этот вопрос есть хороший ответ в вашем собственном вопросе, заданном несколько дней назад: stackoverflow.com/questions/4136900/   -  person John Dibling    schedule 12.11.2010
comment
@John: Это его вопрос, я проголосовал за его закрытие. @Pooria: Если в первой статье говорится, что volatile подходит для многопоточности, то это чушь собачья. Это было объяснено вам в вопросе, на который указал Джон.   -  person sbi    schedule 12.11.2010
comment
@sbi: фактически объяснено, по крайней мере частично, мной   -  person John Dibling    schedule 12.11.2010
comment
Посмотрев статью, о которой идет речь, я беру это назад. Объяснение не помещается в комментарий, поэтому вместо этого я напишу ответ.   -  person sbi    schedule 12.11.2010
comment
@sbi: см. также, что я должен сказать об использовании Александреску volatile. Подводя итог: это не имеет ничего общего с асинхронным доступом или ограничениями памяти, и оно всё связано с предоставлением компилятору возможности обнаруживать места, где вы можете ошибиться. Так же, как когда вы пытаетесь присвоить значение переменной const.   -  person John Dibling    schedule 12.11.2010
comment
@Джон: Где? Если бы я знал, что вы написали такой ответ, это избавило бы меня от работы, чтобы написать его самому.   -  person sbi    schedule 12.11.2010
comment
В другом посте. Хотя ваша формулировка была намного лучше моей. Пожалуйста, оставьте свой ответ без изменений.   -  person John Dibling    schedule 12.11.2010
comment
@John: Я только что украл несколько фраз из твоего очень хорошего комментария выше и вставил их в свой ответ. Я надеюсь, что это хорошо для вас. Редактировать: Неважно. Я сделал свой ответ CW. Я чувствовал себя виноватым, иначе. :)   -  person sbi    schedule 12.11.2010
comment
Я также добавил уточнение в свой ответ в stackoverflow.com/questions/4136900/   -  person John Dibling    schedule 13.11.2010
comment
@John Dibling, Sbi_this вопрос отличается от (stackoverflow.com/questions/4136900/) и, пожалуйста, подведите итоги, чтобы не создавать слишком много сообщений.   -  person Pooria    schedule 13.11.2010
comment
@Pooria: Вот ваше резюме: Pooria: Правда ли, что volatile нужно использовать в многопоточности? Другие: volatile не имеет ничего общего с многопоточностью Pooria: я не спрашиваю о многопоточности   -  person John Dibling    schedule 13.11.2010
comment
@John Dibling_Я спрашиваю о многопоточности, но не о синхронизации доступа к памяти, как вы думаете.   -  person Pooria    schedule 13.11.2010
comment
Насколько я понимаю, объявление чего-то изменчивым только говорит компилятору не кэшировать это значение, поскольку оно может быть изменено в другом месте. Он не говорит компилятору не переупорядочивать код, и в этом основная проблема. Таким образом, объявление volatile может быть необходимо, но одного этого недостаточно для обеспечения потокобезопасности. Что на практике это, вероятно, будет работать из-за того, как реализованы компиляторы, но на самом деле это, вероятно, недостаточно хорошая гарантия! Это правильно?   -  person jcoder    schedule 13.11.2010


Ответы (8)


Я помню, когда эта статья была опубликована, и я помню бесконечные дискуссии, последовавшие затем на comp.lang.c++.moderated.

IIRC, Андрей перехватывает ключевое слово volatile, чтобы использовать его для различения различных перегруженных функций. (См. эту статью Скотта Мейерса еще одна такая идея.) То, что он делает, блестяще, в том, что это позволяет компилятору поймать вас, если вы перепутаете защищенный и незащищенный доступ к объектам (очень так же, как компилятор ловит вас, если вы пытаетесь изменить константу). Но помимо того факта, что это помогает вам, это не имеет ничего общего с реальной защитой одновременного доступа к объектам.

Проблема только в том, что 90% людей бросают один взгляд на статью и все, что они видят, это volatile и "треды" в одной и той же статье. В зависимости от своих знаний они либо делают неправильный вывод о том, что volatile хорош для тредов (вы, кажется, так и сделали), либо кричат ​​на него за то, что он заставляет других рисовать неправильные выводы.
Кажется, что очень немногие люди действительно могут внимательно прочитать статью и понять, что он на самом деле делает.

person Community    schedule 12.11.2010
comment
Это абсолютно, на 100% правильно. Александреску блестяще использует volatile, и я использовал его таким образом с тех пор, как прочитал статью. Мне это очень помогло. Но, честно говоря, я действительно думаю, что эта единственная статья настолько неправильно понята, настолько случайно прочитана, что она нанесла гораздо больше * вреда * современному уровню техники, чем помогла ему. - person John Dibling; 12.11.2010
comment
Ну, разве ты не знаешь. Кто-то с этим не согласился, но постеснялся объяснить, что в этом плохого. Тск, тск. - person sbi; 13.11.2010
comment
@sbi_volatile не имеет ничего общего с реальной защитой одновременного доступа к объектам. Если вы думаете, что я путаю цель использования volatile с использованием объекта синхронизации, вы абсолютно ошибаетесь. - person Pooria; 13.11.2010
comment
@Pooria: Если то, о чем вы здесь спрашиваете, не соответствует тому, что было дано в комментариях к вашему другому вопросу, я до сих пор понятия не имею, о чем вы здесь спрашиваете. Тогда вы, возможно, захотите уточнить свой вопрос. - person sbi; 13.11.2010
comment
@Pooria: Мы здесь не для того, чтобы ссориться с тобой. Мы здесь, чтобы помочь вам. Если мы с sbi отвечаем на вопросы, которые вы не задаете, то пожалуйста, переформулируйте вопрос - person John Dibling; 13.11.2010
comment
@sbi_I сказал, что, как вы думаете, мое намерение отсутствует даже в предыдущем моем вопросе, просто не знаю, почему у вас такое неправильное представление о причине моего вопроса, но слишком много других людей этого не сделали, и я был бы признателен, если бы когда вы думаете, что получили ответ на вопрос, который, как вы думаете, вы понимаете, постарайтесь не делать тему своей. - person Pooria; 13.11.2010
comment
@Pooria: Не буду говорить за sbi, но вот мое замешательство. В этом потоке вы говорите, что если у вас есть переменные примитивного типа, которые должны быть разделены между несколькими потоками, объявите эти переменные volatile. Тогда мы ответим, что volatile не имеет ничего общего с многопоточностью. Тогда вы говорите, что я не спрашивал о многопоточности. Если вы не спрашиваете о многопоточности, то о чем вы спрашиваете? - person John Dibling; 13.11.2010
comment
@John Dibling_Что вы говорите о связи между volatile и кэшированием? - person Pooria; 13.11.2010
comment
@Pooria: Стандарт говорит, что в точках последовательности volatile объекты стабильны. Это означает, что все предыдущие побочные эффекты завершены, и никаких последующих побочных эффектов не возникло. - person John Dibling; 13.11.2010
comment
Кто-то спросил мастера - person Johannes Schaub - litb; 13.11.2010
comment
@John Dibling_Итак, вот оно, одно достижение. - person Pooria; 13.11.2010
comment
@John Dibling - я понимаю, почему Пурия не находит этот ответ полезным. Исходный вопрос заключается не в том, будет ли volatile защищать одновременный доступ к объектам, а в том, необходим ли volatile при использовании многопоточности. Ответ sbi отвечает, что [volatile] не имеет ничего общего с фактической защитой одновременного доступа к объектам, что не отвечает на вопрос. - person danio; 12.09.2011
comment
@danio: Как вы можете заметить, прочитав комментарии в этой ветке, было довольно много путаницы в том, что на самом деле спрашивали. - person John Dibling; 13.09.2011

Я не могу говорить о фактическом сценарии асинхронного доступа, так как я не очень хорошо разбираюсь в многопоточности, но модификатор volatile сообщает компилятору:

«Слушай, это может измениться в любое время, так что не кэшируй это, не помещай в реестр и не делай ничего подобного, ладно?»

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

Изменить: в качестве потенциального примера, который не включает многопоточность (но включает исключительно запутанный код;), вот случай, когда важна изменчивость:

volatile bool keepRunning = true;
void Stuff() {
    int notAPointer = 0;

    notAPointer = (int)(&keepRunning); //Don't do this! Especially on 64-bit processors!

    while(keepRunning) {
        *(bool*)(notAPointer) = false;
    }

    printf("The loop terminated!");
}

Без этого модификатора volatile компилятор может сказать: «Эй, keepRunning никогда не модифицируется, так что мне даже не нужно генерировать код, который его проверяет!», хотя на самом деле мы просто модифицируем его тайно.

(На самом деле это, вероятно, все еще будет работать на неоптимизированной сборке. И это также может работать, если компилятор умен и замечает берущийся указатель. Но принцип тот же)

person Mike Caron    schedule 12.11.2010
comment
+1 @Mike - в основном то, что он делает (насколько я понимаю), заставляет компилятор генерировать код таким образом, чтобы значение просматривалось каждый раз, когда к нему обращаются, поэтому никакого инвариантного подъема цикла или чего-то подобного. - person pstrjds; 12.11.2010
comment
Я добавил пример, демонстрирующий это. - person Mike Caron; 12.11.2010
comment
@Pooria: я надеюсь, что нет: P Никто никогда не должен писать ничего подобного тому, что я только что сделал :) - person Mike Caron; 12.11.2010
comment
Теперь держись. Если keepRunning не модифицируется, это бесконечный цикл, так в чем смысл volatile? - person BЈовић; 12.11.2010
comment
@VJo keepRunning изменяется в разыменованной строке notAPointer в цикле while. Смысл volatile в том, что теоретически компилятор может посмотреть на предоставленный код и определить, что keepRunning не изменен, и, таким образом, не проверять его для каждой итерации цикла (или проверять перед входом в цикл и ничего не делать после этого), volatile должен заставлять проверка keepRunning для каждой итерации. - person pstrjds; 13.11.2010
comment
@VJo: я намеренно обхожу эвристику компилятора. keepRunning IS изменен путем разыменования адреса, который я спрятал в обычное целое число. Компилятор, однако, не может этого знать, поэтому он ДУМАЕТ, что keepRunning не изменяется. volatile сообщает компилятору, что я знаю, что вы знаете, что он никогда не изменялся, но, пожалуйста, поверьте мне в этом. - person Mike Caron; 13.11.2010
comment
А на самом деле это даже не работает. Я не мог заставить MSVC одновременно оптимизировать проверку переменных и косвенно назначать keepRunning, независимо от того, что я делал. Но теория здравая. - person Mike Caron; 13.11.2010
comment
Правила для MSVC находятся по адресу msdn.microsoft.com/en. -us/library/12a04hfd(VS.80).aspx IIRC они несколько строже, чем то, что дает вам С++. Я не думаю, что вы заставите MSVC в режиме оптимизации беспокоиться о записи в энергонезависимый псевдоним, поскольку он может переупорядочить такую ​​запись после завершения цикла, и если вы сделаете псевдоним изменчивым, цикл всегда завершится после одного итерация, поскольку запись упорядочена перед следующим чтением. - person Pete Kirkham; 13.11.2010

Прочтите это. Volatile не имеет ничего общего с многопоточностью.

person BЈовић    schedule 12.11.2010
comment
И еще: в разделе 5 связанной статьи в основном говорится, что Volatile не имеет ничего общего с многопоточным программированием. Статья была написана в соавторстве с тем же человеком, который 3,5 года назад написал, что если у вас есть переменные примитивного типа, которые должны быть разделены между несколькими потоками, объявите эти переменные volatile. - person John Dibling; 12.11.2010
comment
Я могу только догадываться о явной перемене взглядов Александреску, но похоже, что он передумал. Это и хорошо: хотя я не стану утверждать, что это плохо, первоначальный совет определенно был неправильно понят и дал многим программистам ложное чувство безопасности при использовании volatile. - person John Dibling; 12.11.2010
comment
@John: Андрей сильно раскритиковал c.l.c++.m, когда появилась эта первая статья, в основном из-за недопонимания. Я полагаю, это заставило его позже сказать, что Volatile не имеет ничего общего с многопоточным программированием, просто на всякий случай. Его исходная статья по-прежнему верна, но было бы так же правильно, если бы он перехватил volatile, чтобы компилятор сообщал ему, когда он испортил, скажем, строки в системной кодировке и строки UTF-8. Пока что его использование volatile не имеет ничего общего с многопоточностью даже в первой статье. - person sbi; 13.11.2010

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

static volatile bool thread_running = true;

static void thread_body() {
    while (thread_running) {
        // ...
    }
}

static void abort_thread() {
    thread_running = false;
}

В зависимости от того, насколько сложным является thread_body, компилятор может кэшировать значение thread_running в регистре, когда поток начинает работать, что означает, что он никогда не заметит, если значение изменится на false. volatile заставляет компилятор выдавать код, который будет проверять фактическую переменную thread_running в каждом цикле.

person cdhowie    schedule 12.11.2010
comment
@VJo: Что ты имеешь в виду? Конечно, это не имеет прямого отношения к многопоточности, но имеет отношение к асинхронному доступу. - person Mike Caron; 12.11.2010
comment
@VJo: Ты читал газету? В нем обсуждается недостаточность volatile в многопоточной среде. Тем не менее, asnychronous != многопоточность. volatile не имеет отношения к многопоточности, это ДЕЙСТВИТЕЛЬНО связано с асинхронным доступом (который включает в себя такие вещи, как ввод-вывод с отображением памяти). - person Mike Caron; 12.11.2010

Я бы предложил значительно более строгое, но очень полезное правило: если вы не понимаете, что именно делает volatile, не используйте его. Вместо этого используйте lock. Если вы точно не понимаете, что делает lock и как его использовать, не используйте многопоточность.

person jason    schedule 12.11.2010
comment
К сожалению, блокировки без volatile могут привести к тому, что компилятор выполнит оптимизацию, из-за которой ваша программа пропустит обновления правильно заблокированной переменной. - person Mark B; 12.11.2010

Я бы сказал, что теоретически эти правила абсолютно верны, и volatile нужен каждый раз, когда к переменной обращаются 2 потока. (Даже при использовании мьютексов, поскольку они не препятствуют оптимизации компилятора.) Но на практике компиляторы достаточно хорошо распознают ситуации, когда переменная может быть изменена вне конкретной функции, поэтому ее значение не должно кэшироваться в регистрах. В большинстве случаев volatile не требуется.

Однажды я провел некоторое тестирование в MSVC, проверив вывод ассемблера для различных ситуаций, и все, что требовалось для предотвращения кэширования переменной, — это заставить другую функцию писать в ту же переменную или получать ее адрес. Глобальные переменные никогда не оптимизировались, если только не была включена «оптимизация всей программы» (поэтому компилятор может быть уверен, что переменная не изменена в других единицах трансляции).

person Timo    schedule 12.11.2010
comment
@Timo_Итак, вы говорите, просто объявите переменную любого примитивного типа, даже не объявляйте эту переменную, а затем передайте ее адрес другому потоку через API создания потока, эта переменная не будет кэширована? - person Pooria; 13.11.2010

Точно так же стандарт C++ не указывает, как должна работать volatile, вы должны смотреть, что конкретный компилятор делает для конкретной платформы. Кроме того, volatile немного неуловима, и то, что она делает, зависит от компилятора и модели памяти целевой платформы. Замки более интуитивно понятны в использовании.

person Chris O    schedule 12.11.2010

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

person Jerry Coffin    schedule 13.11.2010