Volatile (počítačové programování) - Volatile (computer programming)
v programování, zejména v C, C ++, C#, a Jáva programovací jazyky, nestálý klíčové slovo naznačuje, že a hodnota se může mezi různými přístupy měnit, i když se nezdá, že by byl upraven. Toto klíčové slovo brání optimalizace kompilátoru z optimalizace vzdálených následných čtení nebo zápisů, a tedy nesprávného opětovného použití zastaralé hodnoty nebo vynechání zápisů. Těkavé hodnoty primárně vznikají při přístupu k hardwaru (paměťově mapované I / O ), kde se ke komunikaci používá čtení z nebo zápis do paměti příslušenství a v závitování, kde jiné vlákno mohlo upravit hodnotu.
Přesto, že je běžné klíčové slovo, chování nestálý
se výrazně liší mezi programovacími jazyky a je snadno nepochopen. V C a C ++ je to kvalifikátor typu, jako konst
, a je majetkem typ. Kromě toho v C a C ++ ano ne pracovat ve většině podprocesů scénářů, a toto použití se nedoporučuje. V Javě a C # je to vlastnost a proměnná a naznačuje, že objekt ke kterému je proměnná vázána, může mutovat a je specificky určena pro vlákna. V D programovací jazyk, existuje samostatné klíčové slovo sdílené
pro použití závitů, ale ne nestálý
klíčové slovo existuje.
V C a C ++
V jazyce C a následně v C ++ nestálý
klíčové slovo bylo zamýšleno[1]
- povolit přístup k paměťově mapované I / O zařízení
- povolit použití proměnných mezi
setjmp
alongjmp
- povolit použití
sig_atomic_t
proměnné v obsluhách signálů.
Operace zapnuta nestálý
proměnné nejsou atomový, ani nezakládají řádný vztah „before-before“ pro závitování. To je specifikováno v příslušných normách (C, C ++, POSIX, WIN32),[1] a volatilní proměnné nejsou ve velké většině současných implementací bezpečné pro vlákna. Tedy použití nestálý
klíčové slovo jako přenosný synchronizační mechanismus mnoho skupin C / C ++ odrazuje.[2][3][4]
Příklad paměťově mapovaných I / O v C.
V tomto příkladu kód nastaví hodnotu uloženou v foo
na 0
. Pak to začne hlasování tuto hodnotu opakovaně, dokud se nezmění na 255
:
statický int foo;prázdnota bar(prázdnota) { foo = 0; zatímco (foo != 255) ;}
An optimalizace kompilátoru si všimne, že žádný jiný kód nemůže změnit hodnotu uloženou v foo
, a bude předpokládat, že to zůstane stejné 0
po celou dobu. Kompilátor proto nahradí tělo funkce znakem nekonečná smyčka podobné tomuto:
prázdnota bar_optimized(prázdnota) { foo = 0; zatímco (skutečný) ;}
Nicméně, foo
může představovat umístění, které lze kdykoli změnit jinými prvky počítačového systému, například a hardwarový registr zařízení připojeného k procesor. Výše uvedený kód by takovou změnu nikdy nezjistil; bez nestálý
klíčové slovo, kompilátor předpokládá, že aktuální program je jedinou částí systému, která by mohla změnit hodnotu (což je zdaleka nejběžnější situace).
Aby kompilátor zabránil optimalizaci kódu, jak je uvedeno výše, nestálý
používá se klíčové slovo:
statický nestálý int foo;prázdnota bar (prázdnota) { foo = 0; zatímco (foo != 255) ;}
Při této úpravě nebude podmínka smyčky optimalizována a systém detekuje změnu, když k ní dojde.
Obecně existují paměťová bariéra operace dostupné na platformách (které jsou zveřejněny v C ++ 11), které by měly být upřednostňovány místo volatile, protože umožňují kompilátoru provádět lepší optimalizaci a co je důležitější, zaručují správné chování ve vícevláknových scénářích; specifikace C (před C11) ani specifikace C ++ (před C ++ 11) neurčují paměťový model s více vlákny, takže volatile se nemusí chovat deterministicky napříč operačními systémy / kompilátory / CPU).[5]
Porovnání optimalizace v C.
Následující programy C a doprovodné sestavy ukazují, jak nestálý
klíčové slovo ovlivňuje výstup kompilátoru. Kompilátor v tomto případě byl GCC.
Při dodržování kódu sestavení je jasně viditelné, že se kód generoval pomocí nestálý
Objekty jsou podrobnější, takže je delší, takže jejich podstata je nestálý
objekty mohou být splněny. The nestálý
klíčové slovo brání kompilátoru v provádění optimalizace kódu zahrnujícího těkavé objekty, čímž zajišťuje, že každé přiřazení a čtení těkavých proměnných má odpovídající přístup do paměti. Bez nestálý
klíčové slovo, překladač ví, že proměnná nemusí být při každém použití znovu načtena z paměti, protože by do ní neměly být žádné zápisy z jiného vlákna nebo procesu.
Porovnání sestavy | |
---|---|
Bez nestálý klíčové slovo | S nestálý klíčové slovo |
# include | # include |
gcc -S -O3 -masm = intel noVolatileVar.c -o bez.s | gcc -S -O3 -masm = intel VolatileVar.c -o with.s |
.soubor „noVolatileVar.c“ .intel_syntax noprefix .sekce .rodata.str1.1,„aMS“,@progbits,1.LC0: .tětiva "% d" .sekce .text.startup,"sekera",@progbits .p2align 4,,15 .globl hlavní .typ hlavní, @funkcehlavní:.LFB11: .cfi_startproc sub rsp, 8 .cfi_def_cfa_offset 16 mov esi, 110 mov edi, OFFSET BYT:.LC0 xor eax, eax volání printf mov esi, 200 mov edi, OFFSET BYT:.LC0 xor eax, eax volání printf xor eax, eax přidat rsp, 8 .cfi_def_cfa_offset 8 ret .cfi_endproc.LFE11: .velikost hlavní, .-hlavní .ident „GCC: (GNU) 4.8.2“ .sekce .note.GNU-stack,"",@progbits | .soubor „VolatileVar.c“ .intel_syntax noprefix .sekce .rodata.str1.1,„aMS“,@progbits,1.LC0: .tětiva "% d" .sekce .text.startup,"sekera",@progbits .p2align 4,,15 .globl hlavní .typ hlavní, @funkcehlavní:.LFB11: .cfi_startproc sub rsp, 24 .cfi_def_cfa_offset 32 mov edi, OFFSET BYT:.LC0 mov DWORD PTR [rsp], 10 mov DWORD PTR [rsp+4], 100 mov DWORD PTR [rsp+8], 0 mov DWORD PTR [rsp+12], 0 mov esi, DWORD PTR [rsp] mov eax, DWORD PTR [rsp+4] přidat esi, eax xor eax, eax volání printf mov eax, DWORD PTR [rsp+4] mov edi, OFFSET BYT:.LC0 mov DWORD PTR [rsp], eax mov eax, DWORD PTR [rsp+4] mov DWORD PTR [rsp+8], eax mov eax, DWORD PTR [rsp+4] mov DWORD PTR [rsp+12], eax mov esi, DWORD PTR [rsp+8] mov eax, DWORD PTR [rsp+12] přidat esi, eax xor eax, eax volání printf xor eax, eax přidat rsp, 24 .cfi_def_cfa_offset 8 ret .cfi_endproc.LFE11: .velikost hlavní, .-hlavní .ident „GCC: (GNU) 4.8.2“ .sekce .note.GNU-stack,"",@progbits |
C ++ 11
Podle normy ISO C ++ 11 je klíčové slovo volatile určeno pouze pro přístup k hardwaru; nepoužívejte jej pro komunikaci mezi vlákny. Pro komunikaci mezi vlákny poskytuje standardní knihovna std :: atomic
šablony.[6]
V Javě
The Programovací jazyk Java má také nestálý
klíčové slovo, ale používá se pro trochu jiný účel. Při použití na pole kvalifikátor Java nestálý
poskytuje následující záruky:
- Ve všech verzích Javy existuje globální řazení při čtení a zápisu všech těkavých proměnných (toto globální řazení na těkavých látkách je částečné pořadí přes větší pořadí synchronizace (což je celková objednávka ze všech synchronizační akce)). To znamená, že každý vlákno přístup k těkavému poli přečte před pokračováním jeho aktuální hodnotu namísto (potenciálně) použití hodnoty v mezipaměti. (Neexistuje však žádná záruka relativního řazení těkavých čtení a zápisů s běžnými čteními a zápisy, což znamená, že to obecně není užitečná konstrukce vláken.)
- V prostředí Java 5 nebo novějším volatile čte a zapisuje ustavení a stane se před vztahem, podobně jako získání a vydání mutexu.[7]
Použitím nestálý
může být rychlejší než a zámek, ale před Java 5 to v některých situacích nebude fungovat[8]. Rozsah situací, ve kterých je volatilní efektivní, byl rozšířen v prostředí Java 5; zejména, dvakrát zkontrolováno zamykání nyní funguje správně.[9]
V C #
v C#, nestálý
zajišťuje, že kód pro přístup k poli nepodléhá některým optimalizacím nebezpečným pro vlákna, které mohou být prováděny kompilátorem, CLR nebo hardwarem. Když je pole označeno nestálý
, je kompilátor instruován, aby kolem něj vygeneroval „paměťovou bariéru“ nebo „plot“, což zabrání přeuspořádání instrukcí nebo ukládání do mezipaměti vázaných na pole. Při čtení a nestálý
pole, překladač vygeneruje získat plot, což zabrání přesunutí dalších čtení a zápisů do pole, včetně těch v jiných vláknech před plot. Při psaní do a nestálý
pole, překladač vygeneruje a uvolňovací plot; tento plot brání tomu, aby byla přesunuta další čtení a zápisy do pole po plot.[10]
Lze označit pouze následující typy nestálý
: všechny referenční typy, Singl
, Booleovský
, Byte
, SByte
, Int16
, 16. UInt
, Int32
, UInt32
, Char
a všechny vyjmenované typy s podkladovým typem Byte
, SByte
, Int16
, 16. UInt
, Int32
nebo UInt32
.[11] (To vylučuje hodnotu struktury, stejně jako primitivní typy Dvojnásobek
, Int64
, UInt64
a Desetinný
.)
Za použití nestálý
klíčové slovo nepodporuje pole, která jsou předán odkazem nebo zachycené lokální proměnné; v těchto případech Thread.VolatileRead
a Thread.VolatileWrite
místo toho musí být použito.[10]
Ve skutečnosti tyto metody zakazují některé optimalizace, které obvykle provádí kompilátor C #, kompilátor JIT nebo samotný procesor. Záruky poskytované Thread.VolatileRead
a Thread.VolatileWrite
jsou nadmnožinou záruk poskytovaných nestálý
klíčové slovo: namísto generování "polovičního oplocení" (tj. získání-oplocení zabrání pouze přeskupení instrukcí a ukládání do mezipaměti, které předchází) Volatile Číst
a VolatileWrite
vygenerovat „plný plot“, který zabrání přeskupení instrukcí a mezipaměti tohoto pole v obou směrech.[10] Tyto metody fungují následovně:[12]
- The
Thread.VolatileWrite
metoda vynutí, aby se hodnota v poli zapisovala v místě volání. Kromě toho musí před voláním na dojít k jakémukoli dřívějšímu načtení a uložení objednávky programuVolatileWrite
a jakékoli pozdější načtení a uložení objednávky programu musí nastat po volání. - The
Thread.VolatileRead
metoda vynutí hodnotu v poli, ze které se má číst v místě volání. Kromě toho se před voláním na musí objevit jakékoli dřívější načtení a uložení objednávky programuVolatile Číst
a jakékoli pozdější načtení a uložení objednávky programu musí nastat po volání.
The Thread.VolatileRead
a Thread.VolatileWrite
metody generují plný plot voláním Thread.MemoryBarrier
metoda, která vytváří paměťovou bariéru, která funguje v obou směrech. Kromě výše zmíněné motivace k použití plného oplocení existuje jeden potenciální problém s nestálý
klíčové slovo, které je vyřešeno použitím úplného ohraničení generovaného Thread.MemoryBarrier
je následující: vzhledem k asymetrické povaze polovičních plotů, a nestálý
pole s instrukcí pro zápis následovanou instrukcí pro čtení může mít kompilační příkaz stále přehozený. Protože plné ploty jsou symetrické, při použití to není problém Thread.MemoryBarrier
. [10]
Ve Fortranu
NESTÁLÝ
je součástí standardu Fortran 2003,[13] ačkoli dřívější verze to podporovala jako rozšíření. Vytváření všech proměnných nestálý
ve funkci je také užitečné zjištění aliasing související chyby.
celé číslo, nestálý :: i ! Pokud není definován volatile, jsou následující dva řádky kódu identicképsát si(*,*) i**2 ! Načte proměnnou i jednou z paměti a znásobí tuto hodnotu krát samapsát si(*,*) i*i ! Načte proměnnou i dvakrát z paměti a znásobí tyto hodnoty
Tím, že se kompilátor Fortran vždy „vrtá dolů“ do paměti VOLATILE, vylučuje přeuspořádání čtení nebo zápisů do těkavých látek. To zviditelní ostatní akce podprocesů provedené v tomto vlákně a naopak.[14]
Použití VOLATILE snižuje a může dokonce zabránit optimalizaci.[15]
Reference
- ^ A b „Publikace ve výboru pro standardy C ++“.
- ^ „Volatile Keyword in Visual C ++“. Microsoft MSDN.
- ^ „Dokumentace k jádru Linuxu - Proč by se neměla používat třída„ volatile “. kernel.org.
- ^ Scott Meyers; Andrei Alexandrescu (2004). „C ++ a nebezpečí zamykání s dvojitou kontrolou“ (PDF). DDJ.
- ^ Jeremy Andrews (2007). „Linux: Volatile Superstition“. kerneltrap.org. Archivovány od originál dne 2010-06-20. Citováno 9. ledna 2011.
- ^ „volatile (C ++)“. Microsoft MSDN.
- ^ Oddíl 17.4.4: Pořadí synchronizace„Specifikace jazyka Java®, edice Java SE 7“. Oracle Corporation. 2013. Citováno 2013-05-12.
- ^ Jeremy Manson; Brian Goetz (únor 2004). „JSR 133 (Java Memory Model) FAQ“. Citováno 2019-11-05.
- ^ Neil Coffey. „Double-checked Locking (DCL) and how to fix“. Javamex. Citováno 2009-09-19.
- ^ A b C d Albahari, Joseph. „Část 4: Pokročilé navlékání“. Vlákna v C #. O'Reilly Media. Archivováno (PDF) z původního dne 27. dubna 2011. Citováno 9. prosince 2019.
- ^ Richter, Jeffrey (11. února 2010). „Kapitola 7: Konstanty a pole“. CLR přes C #. Microsoft Press. str.183. ISBN 978-0-7356-2704-8.
- ^ Richter, Jeffrey (11. února 2010). "Kapitola 28: Konstrukce synchronizace primitivních vláken". CLR přes C #. Microsoft Press. str.797 –803. ISBN 978-0-7356-2704-8.
- ^ „VOLATILE Attribute and Statement“. Cray.
- ^ „Volatile and shared array in Fortran“. Intel.com.
- ^ "NESTÁLÝ". Oracle.com.