В чем разница между статической глобальной и статической изменчивой переменной? - PullRequest
28 голосов
/ 06 декабря 2008

Я использовал статическую глобальную переменную и статическую переменную в области видимости файла,

оба обновляются ISR и основным циклом, а основной цикл проверяет значение переменной.

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

Так хорошо ли использовать глобальную переменную вместо volatile?

Есть ли какая-либо конкретная причина для использования статических летучих ??

Любой пример программы будет заметен.

Заранее спасибо ..

Ответы [ 7 ]

42 голосов
/ 06 декабря 2008

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

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

Теперь на энергозависимости:

Как и const, volatile является модификатором типа.

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

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

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

Dr. У Jobb's есть отличная статья о летучих .

Вот пример из статьи доктора Джобба:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

Если компилятор видит, что Sleep() является внешним вызовом, он будет предполагать, что Sleep() не может изменить значение переменной flag_. Таким образом, компилятор может хранить значение flag_ в регистре. И в этом случае это никогда не изменится. Но если другой поток вызывает wakeup, первый поток все еще читает из регистра ЦП. Wait() никогда не проснется.

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

Тот факт, что flag_ была переменной-членом, а не глобальной переменной (и не статической глобальной), не имеет значения. Объяснение после примера дает правильное обоснование, даже если вы имеете дело с глобальными переменными (и статическими глобальными переменными).

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

volatile с указателями:

Volatile с указателями, работает как const с указателями.

Переменная типа volatile int * означает, что переменная, на которую указывает указатель, является изменчивой.

Переменная типа int * volatile означает, что сам указатель является энергозависимым.

21 голосов
/ 06 декабря 2008

Это разные вещи. Я не эксперт по изменчивой семантике. Но я думаю, что имеет смысл то, что описано здесь.

Global

Глобальный просто означает, что рассматриваемый идентификатор объявлен в области видимости файла. Существуют различные области: функция (где определены метки goto), файл (где находятся глобальные переменные), блок (где находятся нормальные локальные переменные) и прототип функции (где находятся параметры функции). Эта концепция просто существует, чтобы структурировать видимость идентификаторов. Это не имеет ничего общего с оптимизацией.

Статический

static - это длительность хранения (мы не будем здесь это рассматривать) и способ присвоения имени, объявленного в пределах внутренней связи области файла. Это можно сделать для функций или объектов, которые требуются только в пределах одного блока перевода. Типичным примером может быть функция help, распечатывающая принятые параметры, которая вызывается только из функции main, определенной в том же файле .c.

6.2.2 / 2 в черновике C99:

Если объявление области видимости файла идентификатор объекта или функции содержит спецификатор класса хранения статический, идентификатор имеет внутренний связь.

Внутренняя связь означает, что идентификатор не виден вне текущей единицы перевода (как функция help выше).

Летучие

Летучие - это другое: ( 6.7.3 / 6 )

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

Стандарт обеспечивает отличный пример для примера, где volatile будет избыточным ( 5.1.2.3 / 8 ):

Реализация может определять взаимно-однозначное соответствие абстрактная и актуальная семантика: в каждая точка последовательности, значения фактические объекты согласуются с те, которые указаны в реферате семантика. Ключевое слово volatile будет излишним.

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

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

Для легкого понимания обзора у Intel есть отличная статья об этом здесь .

Что мне теперь делать?

Продолжайте объявлять ваши глобальные данные области файлов как изменчивые. Глобальные данные сами по себе не означают, что значение переменных будет равно значению, хранящемуся в памяти. А static только делает ваши объекты локальными по отношению к текущему модулю перевода (текущие .c файлы и все остальные файлы # include'ом, которые он использует).

14 голосов
/ 06 декабря 2008

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

Пример:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

Без "volatile" первая запись может быть оптимизирована.

4 голосов
/ 06 декабря 2008

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

Изменчивая переменная в Википедии

3 голосов
/ 06 декабря 2008

Я +1 Фриол ответ. Я хотел бы добавить некоторые уточнения, поскольку в разных ответах, похоже, много путаницы: C volatile не является Java volatile.

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

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

3 голосов
/ 06 декабря 2008

Они могут отличаться в вашей текущей среде, но незначительные изменения могут повлиять на поведение.

  • Разное оборудование (больше процессоров, другая архитектура памяти)
  • Новая версия компилятора с улучшенной оптимизацией.
  • Случайное изменение времени между потоками. Проблема может возникнуть только один раз из 10 млн.
  • Различные настройки оптимизации компилятора.

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

Конечно, если ваша программа не многопоточная, это не имеет значения.

0 голосов
/ 17 июля 2011

volatile переменная означает, что присвоенное ей значение не является константой, то есть если функция, содержащая переменную vol a = 10 и функция добавляет 1 в каждом вызове этой функции, она всегда будет возвращать обновленное значение. { volatile int a=10; a++; } Когда вышеупомянутая функция вызывается снова и снова, переменная a не будет повторно инициализирована до 10, она всегда будет показывать обновленное значение, пока программа не запустится. 1-й выход = 10 тогда 11 тогда 12 и т. д.

...