Programování

Kdy použít volatilní klíčové slovo v C #

Optimalizační techniky používané kompilátorem JIT (just-in-time) v modulu Common Language Runtime mohou vést k nepředvídatelným výsledkům, když se váš program .Net pokouší provádět energeticky nezávislé čtení dat ve scénáři s více vlákny. V tomto článku se podíváme na rozdíly mezi těkavým a energeticky nezávislým přístupem k paměti, na roli těkavého klíčového slova v C # a na to, jak by se těkavé klíčové slovo mělo používat.

Poskytnu několik příkladů kódu v C # pro ilustraci konceptů. Abychom pochopili, jak volatilní klíčové slovo funguje, nejprve musíme pochopit, jak strategie optimalizace kompilátoru JIT funguje v .Net.

Principy optimalizace kompilátoru JIT

Je třeba poznamenat, že kompilátor JIT v rámci optimalizační strategie změní pořadí čtení a zápisů způsobem, který nezmění význam a konečný výstup programu. To je znázorněno na fragmentu kódu uvedeném níže.

x = 0;

x = 1;

Výše uvedený fragment kódu lze změnit na následující - při zachování původní sémantiky programu.

x = 1;

Kompilátor JIT může také použít koncept zvaný „konstantní šíření“ k optimalizaci následujícího kódu.

x = 1;

y = x;

Výše uvedený fragment kódu lze změnit na následující - opět při zachování původní sémantiky programu.

x = 1;

y = 1;

Těkavý vs. energeticky nezávislý přístup k paměti

Paměťový model současných systémů je poměrně komplikovaný. Máte registry procesorů, různé úrovně mezipaměti a hlavní paměť sdílenou více procesory. Když se váš program spustí, procesor může data uložit do mezipaměti a poté přistupovat k těmto datům z mezipaměti, pokud to vyžaduje prováděcí vlákno. Aktualizace a čtení těchto dat mohou běžet proti verzi dat v mezipaměti, zatímco hlavní paměť je aktualizována později. Tento model využití paměti má důsledky pro aplikace s více vlákny.

Když jedno vlákno interaguje s daty v mezipaměti a druhé vlákno se pokouší číst stejná data současně, může druhé vlákno číst zastaralou verzi dat z hlavní paměti. Důvodem je to, že když se aktualizuje hodnota energeticky nezávislého objektu, provede se změna v mezipaměti prováděcího vlákna a ne v hlavní paměti. Když se však aktualizuje hodnota těkavého objektu, provede se nejen změna provedená v mezipaměti prováděcího vlákna, ale tato mezipaměť se poté vyprázdní do hlavní paměti. A když se načte hodnota těkavého objektu, vlákno obnoví svou mezipaměť a přečte aktualizovanou hodnotu.

Použití volatilního klíčového slova v C #

Klíčové slovo volatile v C # se používá k informování kompilátoru JIT, že hodnota proměnné by se nikdy neměla ukládat do mezipaměti, protože by ji mohl změnit operační systém, hardware nebo souběžně provádějící vlákno. Kompilátor se tak vyhne použití jakýchkoli optimalizací proměnné, které by mohly vést ke konfliktům dat, tj. K různým vláknům přistupujícím k různým hodnotám proměnné.

Když označíte objekt nebo proměnnou jako těkavé, stane se kandidátem na těkavé čtení a zápisy. Je třeba poznamenat, že v C # jsou všechny zápisy do paměti těkavé bez ohledu na to, zda zapisujete data do těkavého nebo energeticky nezávislého objektu. Nejednoznačnost se však stane, když čtete data. Když čtete energeticky nezávislá data, spouštěcí vlákno může nebo nemusí vždy získat nejnovější hodnotu. Pokud je objekt nestálý, vlákno vždy získá nejaktuálnější hodnotu.

Proměnnou můžete deklarovat jako těkavou, když ji předložíte znakem nestálý klíčové slovo. Následující fragment kódu to ilustruje.

třídní program

    {

public volatile int i;

static void Main (řetězec [] args)

        {

// Sem napište kód

        }

    }

Můžete použít nestálý klíčové slovo s jakýmkoli typem odkazu, ukazatele a výčtu. Můžete také použít volatilní modifikátor s typy byte, short, int, char, float a bool. Je třeba poznamenat, že místní proměnné nelze deklarovat jako volatilní. Když zadáte objekt referenčního typu jako těkavý, je těkavý pouze ukazatel (32bitové celé číslo, které ukazuje na místo v paměti, kde je objekt skutečně uložen), nikoli hodnota instance. Dvojitá proměnná také nemůže být volatilní, protože má 64 bitů, je větší než velikost slova v systémech x86. Pokud potřebujete vytvořit dvojitou proměnnou volatile, měli byste ji zabalit dovnitř třídy. Můžete to udělat snadno vytvořením třídy obálky, jak je ukázáno v fragmentu kódu níže.

veřejná třída VolatileDoubleDemo

{

private volatile WrappedVolatileDouble volatileData;

}

veřejná třída WrappedVolatileDouble

{

public double Data {get; soubor; }

Všimněte si však omezení výše uvedeného příkladu kódu. I když byste měli nejnovější hodnotu volatileData referenční ukazatel, nemáte zaručenou nejnovější hodnotu Data vlastnictví. Práce na tom je udělat WrappedVolatileDouble typ neměnný.

Ačkoli těkavé klíčové slovo vám může v určitých situacích pomoci při zabezpečení podprocesů, není řešením všech vašich problémů se souběžností podprocesů. Měli byste vědět, že označení proměnné nebo objektu jako těkavého neznamená, že nemusíte používat klíčové slovo lock. Klíčové slovo volatile nenahrazuje klíčové slovo lock. Je tam jen proto, abyste se vyhnuli konfliktům dat, když máte více vláken, které se snaží získat přístup ke stejným datům.