Для чего используется ключевое слово" volatile"?



Я читал некоторые статьи о volatile ключевое слово, но я не мог выяснить его правильное использование. Не могли бы вы сказать мне, для чего он должен использоваться в C# и в Java?

488   7  

7 ответов:

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

Рассмотрим пример:

int i = 5;
System.out.println(i);

компилятор может оптимизировать это, чтобы просто напечатать 5, как это:

System.out.println(5);

однако, если есть другой поток, который может изменить i, это неправильное поведение. Если другой поток изменяется i быть 6, оптимизированная версия будет по-прежнему печатать 5.

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

The ключевое слово volatile имеет разные значения как в Java, так и в C#.

Java

с Спецификация Языка Java:

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

C#

из ссылки C# на ключевое слово volatile:

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

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

  • переменная является энергонезависимой

когда два потока A & B обращаются к энергонезависимой переменной, каждый поток будет поддерживать локальную копию переменной в своем локальном кэше. Любые изменения, сделанные потоком A в его локальном кэше, не будут видны потоку B.

  • переменная летучие

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

Итак, когда сделать переменную летучих?

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

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

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

рассмотрим следующий пример:

something.foo = new Thing();

если foo is переменная-член в классе, и другие процессоры имеют доступ к экземпляру объекта, на который ссылается something, они увидели стоимостью foo изменить до память записывает в Thing конструктор глобально видны! Вот что значит" слабо упорядоченная память". Это может произойти, даже если компилятор имеет все хранилища в конструкторе перед хранилищем в foo. Если foo и volatile тогда в магазин к foo будет иметь семантику выпуска и аппаратное обеспечение гарантирует, что все записи перед записью в foo видны другим процессорам, прежде чем разрешить запись в foo произойти.

как пишет foo быть переупорядочены так плохо? Если строка кэша содержит foo находится в кэше, и магазины в конструкторе пропустили кэш, то это возможно для магазина, чтобы завершить гораздо раньше, чем записи в кэш пропускает.

(ужасная) архитектура Itanium от Intel имел слабо упорядоченную память. Процессор, используемый в оригинальном XBox 360, имел слабо упорядоченную память. Многие процессоры ARM, в том числе очень популярный ARMv7-A имеют слабо упорядоченную память.

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

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

сперла из Википедии:

public class MySingleton {
    private static object myLock = new object();
    private static volatile MySingleton mySingleton = null;

    private MySingleton() {
    }

    public static MySingleton GetInstance() {
        if (mySingleton == null) { // 1st check
            lock (myLock) {
                if (mySingleton == null) { // 2nd (double) check
                    mySingleton = new MySingleton();
                    // Write-release semantics are implicitly handled by marking mySingleton with
                    // 'volatile', which inserts the necessary memory barriers between the constructor call
                    // and the write to mySingleton. The barriers created by the lock are not sufficient
                    // because the object is made visible before the lock is released.
                }
            }
        }
        // The barriers created by the lock are not sufficient because not all threads will
        // acquire the lock. A fence for read-acquire semantics is needed between the test of mySingleton
        // (above) and the use of its contents.This fence is automatically inserted because mySingleton is
        // marked as 'volatile'.
        return mySingleton;
    }
}

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

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

в Java "volatile" используется, чтобы сообщить JVM, что переменная может использоваться несколькими потоками одновременно, поэтому некоторые общие оптимизации не могут быть применены.

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

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

Comments

    Ничего не найдено.