Jump to content

энергозависимый (компьютерное программирование)

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

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

Несмотря на то, что это общее ключевое слово, поведение volatile существенно различается между языками программирования и его легко понять неправильно. В C и C++ это квалификатор типа , например constи является свойством типа . Более того, в C и C++ он не работает в большинстве сценариев многопоточности, и его использование не рекомендуется. В Java и C# это свойство переменной и указывает, что объект , к которому привязана переменная, может видоизменяться, и специально предназначен для потоковой обработки. В языке программирования D есть отдельное ключевое слово shared для использования потоков, но нет volatile ключевое слово существует.

В C и, следовательно, в C++ volatile Ключевое слово было предназначено для: [1]

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

Хотя стандарты C предназначены как для C, так и для C++, они не отражают, что volatile семантика относится к lvalue, а не к объекту, на который ссылается. Соответствующий отчет о дефектах DR 476 (до C11) все еще находится на рассмотрении C17 . [2]

Операции над volatile переменные не являются атомарными и не устанавливают правильных отношений «происходит до» для потоков. Это указано в соответствующих стандартах (C, C++, POSIX , WIN32), [1] и изменчивые переменные не являются потокобезопасными в подавляющем большинстве текущих реализаций. Таким образом, использование volatile Ключевое слово как переносимый механизм синхронизации не рекомендуется многими группами C/C++. [3] [4] [5]

Пример ввода-вывода с отображением в памяти в C

[ редактировать ]

В этом примере код устанавливает значение, хранящееся в foo к 0. Затем он начинает многократно опрашивать это значение, пока оно не изменится на 255:

static int foo;

void bar(void) {
    foo = 0;

    while (foo != 255)
         ;
}

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

void bar_optimized(void) {
    foo = 0;

    while (true)
         ;
}

Однако, foo может представлять собой местоположение, которое может быть изменено другими элементами компьютерной системы в любое время, например, аппаратный регистр устройства, подключенного к ЦП . Приведенный выше код никогда не обнаружит такое изменение; без volatile ключевое слово, компилятор предполагает, что текущая программа — единственная часть системы, которая может изменить значение (что является наиболее распространенной ситуацией).

Чтобы компилятор не оптимизировал код, как указано выше, volatile используется ключевое слово:

static volatile int foo;

void bar (void) {
    foo = 0;

    while (foo != 255)
        ;
}

При таком изменении состояние контура не будет оптимизировано, и система обнаружит изменение, когда оно произойдет.

Как правило, на платформах доступны операции с барьером памяти (которые представлены в C++11), которым следует отдавать предпочтение вместо энергозависимых, поскольку они позволяют компилятору выполнять лучшую оптимизацию и, что более важно, гарантируют правильное поведение в многопоточных сценариях; ни спецификация C (до C11), ни спецификация C++ (до C++11) не определяют многопоточную модель памяти, поэтому Летучая память может не вести себя детерминированно в разных ОС/компиляторах/ЦП. [6]

Сравнение оптимизации в C

[ редактировать ]

Следующие программы на языке C и сопровождающие их фрагменты языка ассемблера демонстрируют, как volatile Ключевое слово влияет на вывод компилятора. Компилятором в данном случае был GCC .

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

Согласно стандарту ISO C++11 , ключевое слово Летучие предназначено только для использования для доступа к оборудованию; не используйте его для межпотоковой связи. Для межпоточного взаимодействия стандартная библиотека предоставляет std::atomic<T> шаблоны. [7]

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

  • Во всех версиях Java существует глобальное упорядочение операций чтения и записи всех изменчивых переменных (это глобальное упорядочение изменчивых переменных является частичным порядком по отношению к большему порядку синхронизации (который представляет собой общий порядок по всем действиям синхронизации )). Это означает, что каждый поток, обращающийся к изменяемому полю, будет считывать его текущее значение перед продолжением вместо (потенциально) использования кэшированного значения. (Однако нет никакой гарантии относительного порядка непостоянных операций чтения и записи по отношению к обычным операциям чтения и записи, а это означает, что в целом это бесполезная конструкция потоков.)
  • В Java 5 или более поздних версиях изменчивые операции чтения и записи устанавливают отношения «происходит до» , во многом похожие на получение и освобождение мьютекса. [8] [9]

С использованием volatile может быть быстрее, чем lock , но в некоторых ситуациях он не будет работать до версии Java 5. [10] В Java 5 диапазон ситуаций, в которых эффективен изменчивый код, был расширен; в частности, блокировка с двойной проверкой теперь работает правильно. [11]

В С# , volatile гарантирует, что код, обращающийся к полю, не подвергается некоторым небезопасным для потоков оптимизации, которые могут выполняться компилятором, средой CLR или аппаратным обеспечением. Когда поле отмечено volatile, компилятор получает указание создать вокруг него «барьер памяти» или «забор», который предотвращает переупорядочение инструкций или кэширование, привязанное к полю. При чтении volatile поле, компилятор генерирует Acquire-Fence , который предотвращает перемещение других операций чтения и записи в поле перед ограждением. При письме в volatile поле компилятор генерирует Release-Fence ; это ограждение предотвращает перемещение других операций чтения и записи в поле после ограждения. [12]

Могут быть отмечены только следующие типы volatile: все ссылочные типы, Single, Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Charи все перечислимые типы с базовым типом Byte, SByte, Int16, UInt16, Int32, или UInt32. [13] (Это исключает структуры значений , а также примитивные типы Double, Int64, UInt64 и Decimal.)

Используя volatile Ключевое слово не поддерживает поля, передаваемые по ссылке или захваченные локальные переменные ; в этих случаях Thread.VolatileRead и Thread.VolatileWrite вместо этого необходимо использовать. [12]

По сути, эти методы отключают некоторые оптимизации, обычно выполняемые компилятором C#, JIT-компилятором или самим процессором. Гарантии, предоставляемые Thread.VolatileRead и Thread.VolatileWrite являются расширенным набором гарантий, предоставляемых volatile ключевое слово: вместо создания «полузабора» (т.е. забор захвата предотвращает только переупорядочение и кэширование инструкций, которые предшествуют ему), VolatileRead и VolatileWrite создать «полный забор», который предотвращает переупорядочение инструкций и кэширование этого поля в обоих направлениях. [12] Эти методы работают следующим образом: [14]

  • The Thread.VolatileWrite метод принудительно записывает значение в поле в момент вызова. Кроме того, любые более ранние загрузки и сохранения в программном порядке должны произойти до вызова VolatileWrite и любые последующие загрузки и сохранения порядка программы должны происходить после вызова.
  • The Thread.VolatileRead метод принудительно считывает значение в поле в момент вызова. Кроме того, любые более ранние загрузки и сохранения в программном порядке должны произойти до вызова VolatileRead и любые последующие загрузки и сохранения порядка программы должны происходить после вызова.

The Thread.VolatileRead и Thread.VolatileWrite методы генерируют полный забор, вызывая метод Thread.MemoryBarrier метод, который создает барьер памяти, работающий в обоих направлениях. В дополнение к мотивам использования полного ограждения, приведенным выше, существует еще одна потенциальная проблема, связанная с volatile ключевое слово, которое решается с использованием полного ограждения, сгенерированного Thread.MemoryBarrier заключается в следующем: из-за асимметричности полузаборов volatile поле с инструкцией записи, за которой следует инструкция чтения, может по-прежнему иметь порядок выполнения, измененный компилятором. Поскольку полные ограждения симметричны, это не является проблемой при использовании Thread.MemoryBarrier. [12]

На Фортране

[ редактировать ]

VOLATILE является частью стандарта Fortran 2003 , [15] хотя более ранняя версия поддерживала его как расширение. Создание всех переменных volatile в функции также полезно найти ошибки, связанные с псевдонимами .

integer, volatile :: i ! When not defined volatile the following two lines of code are identical
write(*,*) i**2  ! Loads the variable i once from memory and multiplies that value times itself
write(*,*) i*i   ! Loads the variable i twice from memory and multiplies those values

Всегда «детализируя» память VOLATILE, компилятор Fortran не может переупорядочивать операции чтения или записи в VOLATILE. Это делает действия, выполненные в этом потоке, видимыми для других потоков, и наоборот. [16]

Использование VOLATILE снижает и даже может помешать оптимизации. [17]

  1. ^ Jump up to: а б «Публикация комитета по стандартам C++» .
  2. ^ Краткое описание запроса на разъяснение для C11. Версия 1.13, октябрь 2017 г.
  3. ^ «Изменчивое ключевое слово в Visual C++» . Microsoft MSDN .
  4. ^ «Документация по ядру Linux – почему не следует использовать класс типа «летучий»» . ядро.орг .
  5. ^ Скотт Мейерс; Андрей Александреску (2004). «C++ и опасности блокировки с двойной проверкой» (PDF) . ДДЖ .
  6. ^ Джереми Эндрюс (2007). «Linux: нестабильное суеверие» . kerneltrap.org. Архивировано из оригинала 20 июня 2010 г. Проверено 9 января 2011 г.
  7. ^ «летучий (C++)» . Microsoft MSDN .
  8. ^ Раздел 17.4.4: Порядок синхронизации. «Спецификация языка Java®, Java SE 7 Edition» . Корпорация Оракл . 2013 . Проверено 12 мая 2013 г.
  9. ^ «Параллелизм Java: понимание «изменчивого» ключевого слова» . dzone.com. 08.03.2021. Архивировано из оригинала 9 мая 2021 г. Проверено 9 мая 2021 г.
  10. ^ Джереми Мэнсон; Брайан Гетц (февраль 2004 г.). «Часто задаваемые вопросы по JSR 133 (модель памяти Java)» . Архивировано из оригинала 9 мая 2021 г. Проверено 5 ноября 2019 г.
  11. ^ Нил Коффи. «Блокировка с двойной проверкой (DCL) и способы ее устранения» . Явамекс . Проверено 19 сентября 2009 г.
  12. ^ Jump up to: а б с д Альбахари, Джозеф. «Часть 4: Расширенная обработка потоков» . Потоки в C# . О'Рейли Медиа. Архивировано из оригинала 12 декабря 2019 года . Проверено 9 декабря 2019 г. {{cite web}}: CS1 maint: bot: исходный статус URL неизвестен ( ссылка )
  13. ^ Рихтер, Джеффри (11 февраля 2010 г.). «Глава 7: Константы и поля». CLR через C# . Майкрософт Пресс. стр. 183 . ISBN  978-0-7356-2704-8 .
  14. ^ Рихтер, Джеффри (11 февраля 2010 г.). «Глава 28: Примитивные конструкции синхронизации потоков». CLR через C# . Майкрософт Пресс. стр. 797–803 . ISBN  978-0-7356-2704-8 .
  15. ^ «ИЗМЕНЯЕМЫЙ Атрибут и утверждение» . Крей. Архивировано из оригинала 23 января 2018 г. Проверено 22 апреля 2016 г.
  16. ^ «Изменчивый и общий массив в Фортране» . Intel.com .
  17. ^ «ЛЕГКИЙ» . Oracle.com .
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: b5639e90c975cde058a7ed5fcb83acde__1701294060
URL1:https://arc.ask3.ru/arc/aa/b5/de/b5639e90c975cde058a7ed5fcb83acde.html
Заголовок, (Title) документа по адресу, URL1:
volatile (computer programming) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)