Після дивлячись на купу з інших питань і їх відповідей , я отримую враження , що не існує ніякого широко поширеного угоди про те , що «летючий» ключове слово в C означає точно.
Навіть сам стандарт не здається достатньо зрозумілим, щоб усі могли погодитися, що це означає .
Серед інших проблем:
- Схоже, вони надають різні гарантії залежно від обладнання та залежно від вашого компілятора.
- Це впливає на оптимізацію компілятора, але не на апаратні оптимізації, тому на просунутому процесорі, який робить власні оптимізації під час роботи, навіть не ясно, чи може компілятор запобігти будь-якій оптимізації, яку ви хочете запобігти. (Деякі компілятори генерують інструкції, щоб запобігти оптимізації апаратних засобів у деяких системах, але це, схоже, не стандартизовано.)
Підсумовуючи проблему, виявляється (багато читаючи), що "мінливий" гарантує щось на кшталт: Значення буде прочитане / записане не просто з / в реєстр, а принаймні до кеш-пам'яті L1 ядра в тому ж порядку, що читання / запис відображається в коді. Але це здається марним, оскільки читання / запис з / в регістр вже достатньо в одному потоці, тоді як узгодження з кешем L1 не гарантує нічого більше щодо координації з іншими потоками. Я не уявляю, коли це колись може бути важливим для синхронізації лише з кешем L1.
ВИКОРИСТАННЯ 1
Єдиним широко узгодженим використанням летучих, здається, є для старих або вбудованих систем, де певні місця пам'яті апаратно відображаються на функції вводу / виводу, як біт у пам'яті, що управляє (безпосередньо в апаратному забезпеченні) світлом , або трохи пам’яті, яка вказує на те, чи клавіша клавіатури відключена чи ні (тому що вона підключена апаратним забезпеченням безпосередньо до клавіші).
Здається, що "використання 1" не відбувається в портативному коді, цілі якого включають багатоядерні системи.
ВИКОРИСТАННЯ 2
Не надто відрізняється від "використання 1" - це пам'ять, яку в будь-який час можна прочитати або записати обробником переривання (який може керувати світлом або зберігати інформацію від ключа). Але вже для цього ми маємо проблему, що залежно від системи обробник переривання може працювати на іншому ядрі з власним кешем пам'яті , а "непостійний" не гарантує когерентності кешу у всіх системах.
Тож "використання 2", здається, виходить за рамки того, що може досягти "мінливий".
ВИКОРИСТАННЯ 3
Єдине інше безперечне використання, яке я бачу, - це запобігати неправильній оптимізації доступу через різні змінні, що вказують на ту саму пам'ять, яку компілятор не усвідомлює, - це та сама пам'ять. Але це, мабуть, лише беззаперечно, тому що люди не говорять про це - я бачив лише одну згадку про це. І я подумав, що стандарт C вже визнав, що "різні" покажчики (наприклад, різні аргументи на функцію) можуть вказувати на один і той же елемент або сусідні елементи, і вже вказав, що компілятор повинен створювати код, який працює навіть у таких випадках. Однак я не зміг швидко знайти цю тему в останньому (500 сторінок!) Стандарті.
Тож "використання 3" можливо взагалі не існує ?
Звідси моє запитання:
Чи гарантує "непостійний" взагалі що-небудь в портативному коді С для багатоядерних систем?
EDIT - оновлення
Після перегляду останнього стандарту виглядає так, що відповідь принаймні дуже обмежений так:
1. Стандарт багаторазово визначає спеціальну обробку для конкретного типу "летюча sig_atomic_t". Однак стандарт також говорить, що використання сигнальної функції в багатопотоковій програмі призводить до невизначеної поведінки. Тож цей випадок використання здається обмеженим зв’язком між однопотоковою програмою та її обробником сигналу.
2. Стандарт також визначає чітке значення для "летючого" стосовно setjmp / longjmp. (Приклад коду, де це має значення, наведено в інших питаннях та відповідях .)
Тож стає більш точним питанням:
чи гарантує "непостійний" взагалі що-небудь у переносному коді С для багатоядерних систем, крім (1), що дозволяє однопотоковій програмі отримувати інформацію від свого обробника сигналів, або (2) дозволяє setjmp код, щоб побачити змінні, змінені між setjmp та longjmp?
Це все ще так / ні питання.
Якщо "так", було б чудово, якби ви могли показати приклад портативного коду без помилок, який стає баггі, якщо "простійний" пропущено. Якщо "ні", то, гадаю, компілятор може ігнорувати "мінливі" поза цими двома дуже конкретними випадками для багатоядерних цілей.
volatile
яке йде мова, стосується конкретно, що я вважаю необхідним.
volatile
щоб повідомити програму, що вона може змінюватися асинхронно.