tag:blogger.com,1999:blog-10303035.post114590337855386929..comments2024-02-04T23:20:04.066+03:00Comments on Алёна C++: Ключевое слово volatileAlenahttp://www.blogger.com/profile/09389124127364799922noreply@blogger.comBlogger66125tag:blogger.com,1999:blog-10303035.post-72098492070753807362020-07-16T10:54:04.189+03:002020-07-16T10:54:04.189+03:00Весьма разумно, использование C++20 на каком-нибуд...Весьма разумно, использование C++20 на каком-нибудь простеньком микроконтроллере без операционной системы и с использованием прерываний напрямую - мягко говоря неактуально. Уже с C++11 появились фичи, позволяющие обойтись без прямого использования volatile практически во всех ситуациях (за исключением очень не рекомендуемых применений этого модификатора). Оставляем volatile для программирования на C и C++ ниже 11-го для непосредственного программирования прерываний в одноядерных микроконтроллерах (с ограниченными ресурсами).Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-28972264460055194002020-06-30T18:32:01.966+03:002020-06-30T18:32:01.966+03:00В сисиплюс 20 волатиле убирают, вроде бы как бы
Бо...В сисиплюс 20 волатиле убирают, вроде бы как бы<br />Больше такого модификатора не будетСершhttps://www.blogger.com/profile/07088628889981769370noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-37846715847235008972015-10-20T15:49:14.023+03:002015-10-20T15:49:14.023+03:00Конечно volatile был сделан в первую очередь для т...Конечно volatile был сделан в первую очередь для тех переменных, которые могут меняться в прерываниях и его использование сильно привязано к архитектуре. В серьёзном проекте на C++ (не привязаннном к конкретной архитектуре и операционной системе) использование volatile не желательно. Конечно это ключевое слово подходит для устранения лишнего предупреждения во время компиляции или убедить компилятор не выкидывать определённый код, но при этом больше похоже на "костыль", чем на серьёзное программирование. Одна из проблем использования volatile - осложняется поддержка кода (особенно другими программистами, которые не в курсе для чего этот костыль).<br />Вывод: volatile нужно знать и не использовать (если конечно это не системное программирование с использованием прерываний процессора)!Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-5287708735315318182015-10-15T10:26:32.768+03:002015-10-15T10:26:32.768+03:00Эх... а мне вот приходилось volatile использовать ...Эх... а мне вот приходилось volatile использовать и без всякого "волшебного" изменения переменной.<br />Была у меня такая вот строка:<br />bufer_lenght=((uint32_t)bufread[5]<<24) + ((uint32_t)bufread[6]<<16) + ((uint32_t)bufread[7]<<8) + (uint32_t)bufread[8];<br /><br />Если bufer_lenght не объявить как volatile, то строка вообще выкидывалась компилятором из программы. (а я еще не с первого раза понял, почему bufer_lenght у меня все время равен нулю, хотя не должен был).Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-78942820730252596252015-09-10T16:29:49.310+03:002015-09-10T16:29:49.310+03:00Теперь банановый!
volatile function https://stacko...Теперь банановый!<br />volatile function https://stackoverflow.com/questions/15283223/volatile-functionAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-89584834024387921902015-05-14T20:42:16.213+03:002015-05-14T20:42:16.213+03:00Я столкнулся с такой ситуацией. Есть стековый объе...Я столкнулся с такой ситуацией. Есть стековый объект, указатель на который будет передаваться другим потокам для работы с ним. Объект далее нигде не используется текущим потоком. Сработает ли какая-нибудь там оптимизация, может ли объект удалиться раньше, чем другие потоки отработают? Lifetime optimization? И насколько может помочь volatile?<br />GameState state;<br />copy_state (state, original_state);<br />threads[0].state = &state;<br />threads[1].state = &state;<br />// Все, дальше state не используется в данном потоке.<br />for (...)<br />{<br /> send_thread_command(...);<br />};<br />WaitForMultipleObjects(...);<br />// А потокам он нужен вплоть до сюдаAnonymoushttps://www.blogger.com/profile/03478470637477880579noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-12445893048122494262014-11-20T19:45:35.116+03:002014-11-20T19:45:35.116+03:00Еще полезное применение - отладка. Очень часто ком...Еще полезное применение - отладка. Очень часто компилятор оптимизирует переменные, которые хочется посмотреть в отладчике (Eclipse gcc). С volatile такого не происходит.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-27303195808347841862014-04-25T13:45:26.027+04:002014-04-25T13:45:26.027+04:00Главным образом это слово используется при програм...Главным образом это слово используется при программировании микроконтроллеров. Там часто бывает что происходят асинхронные события (например кто-то нажал кнопку или пришел пакет по какому-либо интерфейсу) Компилятор же о таких вещах не подозревает, и во всю оптимизирует твои сложные конструкции для парсинга и проверки пакета, как ненужные. Массив даных же всегда пустой, по его мнению.Anonymoushttps://www.blogger.com/profile/11208765801464018296noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-27781119265475842392013-03-19T12:10:39.147+04:002013-03-19T12:10:39.147+04:00http://habrahabr.ru/company/abbyy/blog/161607/
вот...http://habrahabr.ru/company/abbyy/blog/161607/<br />вот зачемAnonymoushttps://www.blogger.com/profile/06767670903612695304noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-72580334119107531812012-11-22T16:58:06.796+04:002012-11-22T16:58:06.796+04:00В многопочточных программах есть своя специфика ис...В многопочточных программах есть своя <a href="http://hashcode.ru/questions/165619#165623" rel="nofollow">специфика использования volatile</a>.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-32943953407116936982012-10-11T13:09:47.413+04:002012-10-11T13:09:47.413+04:00В WinAPI все функции семейства InterlockedXXX треб...<i>В WinAPI все функции семейства InterlockedXXX требуют, чтобы изменяемый параметр был volatile.</i><br />Цитата из книги Рихтера - Виндоуз для профессионалов.<br /><br /><i>Вас, наверное, заинтересовало, а не следует ли объявить как volatile и мою переменную g_fResourcelnUse в примере со спин-блокировкой. Отвечаю: нет, потому что она передается Interlocked-функции по ссылке, а не по значению. Передача перемен ной по ссылке всегда заставляет функцию считывать ее значение из памяти, и оптимизатор никак не влияет на это.<br /></i>Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-82250987778416104432012-10-09T13:03:30.879+04:002012-10-09T13:03:30.879+04:00По поводу "А если ее лочить, то и volatile не...<i>По поводу "А если ее лочить, то и volatile не нужен. Доступ к переменным может лочиться по-разному, но по тому, что я читала в форумах, где бы вы ни лочили, volatile в дополнение к локу указывать не нужно. Более того, использование volatile в данной ситуации может сказаться на производительности."<br /><br />volatile не нужен по той простой причине, что любая блокировка является барьером памяти (иначе работать она не будет) и необходимость в volatile отпадает.</i><br /><br />В WinAPI все функции семейства <b>InterlockedXXX</b> требуют, чтобы изменяемый параметр был volatile.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-27531572755164223342012-08-27T11:43:19.671+04:002012-08-27T11:43:19.671+04:00По поводу "А если ее лочить, то и volatile не...По поводу "А если ее лочить, то и volatile не нужен. Доступ к переменным может лочиться по-разному, но по тому, что я читала в форумах, где бы вы ни лочили, volatile в дополнение к локу указывать не нужно. Более того, использование volatile в данной ситуации может сказаться на производительности."<br /><br />volatile не нужен по той простой причине, что любая блокировка является барьером памяти (иначе работать она не будет) и необходимость в volatile отпадает.<br /><br />volatile был введен в стандарт для урегулирования проблем с MMIO, когда к примеру запись типа<br /><br />*ptr = 1;<br />*ptr = 2;<br /><br />могла быть оптимизирована и первое присваивание могло быть удалено оптимизатором, но на самом деле каждая запись отправлялась на некий device.<br />А в многопоточном приложении ему делать нечего. Атомарности он не гарантирует (в отличии от Visual C++), проблему с memory ordering-ом не решает.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-48241796338478611032012-08-03T20:16:44.271+04:002012-08-03T20:16:44.271+04:00В старых системах и всех современных НЕ конвеерных...В старых системах и всех современных НЕ конвеерных МК volatile означает, что переменная будет в ОЗУ и все операции с ней будут проводится там.<br />Процессор имеет регистры (нынче модно называть кэш). Чтобы выполнить операцию, он сначала подгружает переменную в регистр, в регистре выполняет операцию, потом возвращает её в ОЗУ. Причём в регистре переменная может висеть очень долго, если с ней идёт много работы. И вот тут возникло прерывание. В прерывании изменяется та же переменная. Берётся она ИЗ ОЗУ (ибо обработчик не может знать что вот прямо сейчас мы с ней что-то делаем в кэше), обрабатывается и возвращается взад. Мы возвращаемся в основной код и продолжаем работать с копией исходной переменной в регистрах. Окончив эту работу, возвращаем переменную в ОЗУ, переписав тем самым результат работы прерывания. Последствия могут быть самыми чудесными.<br />Если же переменная volatile, она не будет подгружена в регистр и всегда будет в одном и том же месте, а значит её всегда там можно найти из любого обработчика и все изменения будут немедленно отображены для остальных. <br />То, что компилятор не оптимизирует volatile лишь побочных эффект данной особенности.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-86248361206589419752012-07-31T18:33:38.503+04:002012-07-31T18:33:38.503+04:00Из примера:
"unsigned char* pControl = 0xff24...Из примера:<br />"unsigned char* pControl = 0xff24;"<br /><br />Как вы в 1 байт запихиваете 0xff24, если у него предел 0xFF?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-60437215821024278282012-02-10T19:07:50.879+04:002012-02-10T19:07:50.879+04:00Только что в gcc встретил:
- пишу защитный механи...Только что в gcc встретил:<br /><br />- пишу защитный механизм, переменная N содержит адрес перехода, он может меняться в коде в разных функциях, сам же код собирается из множества этих функций в одну огромную посредством inline, ну и в самом конце делается вызов функции по адресу этой переменной N. Так вот компилятор так это дело заоптимизировал, что данная переменная всегда содержала только одно значение которое в нее устанавливалось в самом начале. т.е. по коду идет установка, изменение переменной, но в конце вызов всегда происходил по начальному значению, само значение переменной просто не использовалось. Вот такой оригинальный баг :) <br /><br />volatile конечно все исправила :) Будьте внимательны!Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-13615758350797611392011-12-23T23:04:22.475+04:002011-12-23T23:04:22.475+04:00>>Любой Lock будет в своей реализации содерж...>>Любой Lock будет в своей реализации содержать "volatile-переменную", это заставит компилятор отменить перестановку инструкций.<br />volatile - это просто инструкция компилятору читать из памяти значение при каждом обращении и всё. никаких других допущений в общем случае делать нельзя.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-29053121874832559622011-12-20T03:35:20.710+04:002011-12-20T03:35:20.710+04:00>"Помоему это неверно. volatile нужно указ...>"Помоему это неверно. volatile нужно указывать в дополнение к локу "<br /><br />Пришел к выводу что это утверждение неверное, потому что:<br />Любой Lock будет в своей реализации содержать "volatile-переменную", это заставит компилятор отменить перестановку инструкций. То есть освобождение lock произойдет гарантировано после выполнения предыдйщих инструкций с исходном коде. Скорее всего приведеный в блоге пример, где это не так (mutex.leave()) содержал неправильную реализацию mutexircicqhttps://www.blogger.com/profile/09461345578113522538noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-74514953896159527362011-09-15T07:23:34.235+04:002011-09-15T07:23:34.235+04:00Вы правы относительно скорости, поэтому я и делал ...Вы правы относительно скорости, поэтому я и делал уточнение: "не критично по скорости пересылки". Я использовал данный метод для вывода на экран сколько процентов работы выполнено (прогресс). Эта информация не столь критична по скорости (задержка даже в секунду здесь была не принципиальна, а так как постоянно был поток данных, кеш обновлялся довольно быстро и данные там не залёживались). На процессорах архитектуры x86 для синхронизации процессов можно использовать ассемблерную команду LOCK, которая блокирует шину на время выполнения одной команды, гарантируя, что при этом операция не будет прервана. При блокировке операций "чтение-модификация" мы можем гарантированно потокобезапасно например прибавить к переменной общего пользования какое-то значение (например увеличить на 1). И другие подобные операции. Причём это такие "вкусности", что это было включено в стандарт C++11 (так что это имеет непосредственное отношение к C++). Там есть такие функции, которые могут потокобезопасно увеличить значение переменной, уменьшить и подобно (то, что позволяет сделать ассемблер x86 с помощью команды LOCK). Этой командой можно реализовать и синхронизацию обращения разных потоков к общим переменных. Так например критические секции в студии используют именно команду LOCK, а не элементы ядра.<br />Если вы программист C++ и не хотите влазить в тонкости ассемблера, то просто берите стандарт C++11 и используйте эти особенности процессоров x86 не вдаваясь в тонкости реализации. Просто помните, что на процессорах другой архитектуры стандартные операции C++11 потокобезопасного сложения и вычитания и другие (которые для реализации используют именно ассемблерную команду LOCK) могут работать заметно дольше.Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-29445246679308574692011-09-15T00:02:53.799+04:002011-09-15T00:02:53.799+04:00to Екклесиаст ..
"При этом мы имеем 100% защ...to Екклесиаст ..<br /><br />"При этом мы имеем 100% защиту переменных для передачи без использования объектов ядра и независимо от архитектуры."<br /><br />При таком методе каждый из параллельных потоков должен циклиться в ожидании допустимого для него значения переменой.<br />volatile гарантирует, что компилятор сгенерирует при обращении к этой переменной команду чтения из памяти.<br />Но... память бывает разная, в том числе и кэшируемая.<br />Поэтому считывание может происходить не из самой памяти, а из кэша процессора (так же, как и операция записи может писать в кэш), и компилятор может этим не управлять.<br />Часто процессоры имеют собственные (не разделяемые с другими) кэши и на некоторых архитектурах даже не синхронизируемые автоматически (аппаратно).<br />На такой архитектуре для синхронизации кэшей без специальных действий (или операционной системы) не обойтись.<br /><br />Поэтому при предлагаемом методе возможна ситуация, когда один поток изменяет переменную, а другой поток этого "не видит", так как считывает ее старое значения из кэша процессора. В результате он может очень долго циклиться, несмотря на то, что доступ к общим данным ему разрешен.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-10303035.post-60337113406016589832011-09-02T12:40:19.760+04:002011-09-02T12:40:19.760+04:00to Дима комментирует...
Ну если на то идти, то пра...to Дима комментирует...<br />Ну если на то идти, то практически все случаи использования volatile можно заменить геттерами и сеттерами, в этом случае мы уходим от проблемы изменчивости переменных ;) и этот пример не исключение (язык C++ тяготеет к подобному методу использования переменных, чтобы защитить их от шаловливых ручек).<br />Но если мы сделаем эти функции инлайновскими - нет гарантии, что компилятор их не оптимизирует непредсказуемо, а если спрячем - будут накладные расходы по вызову функций. Конечно, если расходы по вызову функции составляют множество меры ноль по отношению ко времени выполнения остального кода, то использование функций предпочтительнее.<br />P.S. Говоря про искажение моего кода я говорил именно про:<br />1: while(var == LOCKED);<br />2: var = LOCKED;<br />У меня в коде подобное отсутствовало...Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-91525671599398278832011-09-02T12:15:56.832+04:002011-09-02T12:15:56.832+04:00to Екклесиаст ..
> Вы привели совсем другой код...to Екклесиаст ..<br />> Вы привели совсем другой код<br />Ну, не знаю, не знаю. Вы свой стерли, исправили, запостили заново.<br /><br />Касаемо вашего примера. Вы пытаетесь упростить ситуацию. Хорошо, давайте упрощать.<br />Заводим с-модуль в котором объявляем:<br />int flag = 0;<br />int get_flag() {return flag;}<br />void set_flag(int _flag) {flag = _flag;}<br />все остальные видят методы через заголовочный файл. Если не используем multifile compilation, то у компилятора не будет возможности "кэшировать" значение flag в регистрах. Таким образом все будет работать без volatile.<br /><br />Далее, ваш пример работает ровно до тех пор пока flag разграничивает два потока, добавьте третий и работать не будет.<br /><br />Я согласен с тем, что простые ситуации надо разруливать простыми методами, но к системному подходу это не имеет никакого отношения.Димаhttps://www.blogger.com/profile/10909008956859530381noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-24094258685036835422011-09-02T12:12:51.686+04:002011-09-02T12:12:51.686+04:00Этот комментарий был удален автором.Димаhttps://www.blogger.com/profile/10909008956859530381noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-26590639079271925222011-09-02T11:08:13.426+04:002011-09-02T11:08:13.426+04:00to Дима комментирует...
Вы привели совсем другой ...to Дима комментирует...<br /><br />Вы привели совсем другой код, естественно если для синхронизации использовать две атомарных операции (как у вас написано), а не одну - тогда можно получить крах.<br />Пример синхронизации:<br />У нас есть два потока и один должен другому слать информацию (не критично по скорости пересылки, главное чтобы потоки на этом не тормозили, а делали своё дело). Заводим переменную volatile char flag=0; и переменные, которые нужно защитить.<br />Делаем двухтактную передачу данных:<br />1) Один процесс может устанавливать flag только в 1, а другой только в 0.<br />2) Первый процесс может работать с защищаемыми переменными только когда flag==0 и по окончанию работы устанавливает flag=1.<br />3) Второй процесс может работать с защищаемыми переменными только когда flag==1 и по окончанию работы устанавливает flag=0.<br />При этом мы имеем 100% защиту переменных для передачи без использования объектов ядра и независимо от архитектуры.<br />Если вы найдёте реальную дыру в этом методе - буду рад здравым замечаниям (сразу говорю, что подобный метод не заменит например мьютексы, но в конкретном случае работает).Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.comtag:blogger.com,1999:blog-10303035.post-9165731859023946772011-08-30T13:59:26.893+04:002011-08-30T13:59:26.893+04:00Я не вижу проблем использовать для синхронизации п...Я не вижу проблем использовать для синхронизации потоков переменные объявленные как volatile. Но какие типы при этом можно использовать зависит от платформы и компилятора. Например в 32-х разрядных системах операции с 32-х разрядными числами (если они выравнены по границе слова) можно считать атомарными( для записи и чтения). В современных компьютерах не используется шина данных меньше 32-х бит, а значит запись 32-х битного слова произходит за один такт и не может быть разделена на несколько операций. Только если в памяти 32-х битная переменная не выравнена по границе слова, то она считывается и записывается за 2 приёма.<br />Компилятор по умолчанию (если мы не укажем ему иное) int выравнивает по границе слова, значит чтение и запись тапа int - атомарная операция. То же самое относится к типу bool. При использовании типа char у нас всегда атомарная операция при чтении или записи. И поэтому если один поток пишет в volatile char, а другой только читает - у нас нет проблемм. Если мы уверены, что volatile int выравнено по границе слова, то при записи в неё одним потоком и чтении другим - мы всегда будем получать корректный результат.<br />Но есть и подводные камни, например:<br />volatile bool res=false;<br />...<br />while( res ) Sleep(10);<br />Даже если другой поток изменяет значение res на true (пока выполняется код перед циклом), то цикл может и не начать работать, а программа проскочет это место, словно цика и небыло (это зависит от компилятора... ну может ещё от фазы луны :), сам недавно сталкивался с данной проблемой при написании многопоточной программы на Builder C++ 2009. Кстати если код изменить:<br />while( 1 ) {<br /> if( !res ) break;<br /> Sleep(10);<br />}<br />То всё работает без проблем.<br />Хотя возможно, если человек незнаком с тем как работает шина данных, кеш, как процессор считыват данные из оперативной памяти, то ему лучше ничего кроме volatile char и не использовать в многопоточном программировании без использования специальных способов защиты целостности данных (мьютексы, критические секции и подобное).Екклесиастhttps://www.blogger.com/profile/15041060857610372697noreply@blogger.com