Sdílet prostřednictvím


volatile (Referenční dokumentace jazyka C#)

Klíčové volatile slovo označuje, že pole může být změněno více vlákny, která se spouští současně. Kompilátor, systém runtime a dokonce i hardware může změnit uspořádání čtení a zápisů do umístění paměti z důvodu výkonu. Deklarovaná pole jsou vyloučena volatile z určitých druhů optimalizací. Neexistuje žádná záruka jednoho celkového pořadí volatilních zápisů, jak je viděno ze všech vláken vykonávání. Další informace najdete ve třídě Volatile.

Upozornění

Klíčové volatile slovo je často špatně pochopeno a zneužito při vícevláknovém programování. Ve většině scénářů byste měli místo nich používat bezpečnější a spolehlivější alternativy volatile. Moderní .NET poskytuje lepší nástroje souběžnosti, jako je Interlocked třída, lock příkaz nebo primitiva synchronizace vyšší úrovně. Tyto alternativy poskytují jasnější sémantiku a silnější záruky než volatile. Zvažte použití volatile pouze ve výjimečných pokročilých scénářích, ve kterých plně rozumíte jeho omezením a ověřili jste, že se jedná o vhodné řešení.

Poznámka:

V multiprocesorovém systému nestálá operace čtení nezaručuje získání nejnovější hodnoty zapsané do umístění paměti jakýmkoli procesorem. Podobně nestálá operace zápisu nezaručuje, že hodnota zapsaná by byla okamžitě viditelná pro ostatní procesory.

Klíčové volatile slovo lze použít u polí těchto typů:

  • Odkazové typy.
  • Typy ukazatelů (v nebezpečném kontextu). Všimněte si, že i když samotný ukazatel může být nestálý, objekt, na který odkazuje, nemůže. Jinými slovy, nemůžete deklarovat "ukazatel na volatile".
  • Jednoduché typy jako sbyte, , byte, short, ushortintuintchar, float, a .bool
  • Typ enum s jedním z následujících základních typů: byte, sbyte, shortushort, , int, nebo uint.
  • Parametry obecného typu známé jako odkazové typy.
  • IntPtr a UIntPtr.

Jiné typy, včetně double a long, nelze označit volatile , protože čtení a zápisy do polí těchto typů nelze zaručit atomické. Pokud chcete chránit přístup u těchto typů polí ve vícevláknovém režimu, použijte členy třídy Interlocked nebo chraňte přístup pomocí příkazu lock.

U většiny scénářů s více vlákny, i s podporovanými typy, preferujte použití Interlocked operací, lock příkazů nebo jiných primitiv synchronizace místo volatile. Tyto alternativy jsou méně náchylné k drobným chybám souběžnosti.

Klíčové volatile slovo lze použít pouze u polí typu class nebo struct. Místní proměnné nelze deklarovat volatile.

Alternativy k nestálé

Ve většině případů byste měli místo volatile použít některou z těchto bezpečnějších alternativ:

  • Interlocked operace: Poskytují atomické operace pro číselné typy a přiřazování odkazů. Obvykle jsou rychlejší a poskytují silnější záruky než volatile.
  • lock příkaz: Poskytuje vzájemné vyloučení a paměťové bariéry. Slouží k ochraně větších kritických sekcí.
  • Volatile třída: Poskytuje explicitní operace čtení a zápisu pro volatile s jasnější sémantikou než volatile klíčové slovo.
  • Primitivy synchronizace vyšší úrovně: například ReaderWriterLockSlim, Semaphorenebo souběžné kolekce z System.Collections.Concurrent.

Klíčové volatile slovo neposkytuje atomicitu pro operace jiné než přiřazení, nezabraňuje vzniku závodních podmínek a neposkytuje záruky pořadí pro jiné operace paměti. Díky těmto omezením není vhodná pro většinu scénářů souběžnosti.

Příklad

Následující příklad ukazuje, jak deklarovat proměnnou veřejného pole jako volatile.

class VolatileTest
{
    public volatile int sharedStorage;

    public void Test(int i)
    {
        sharedStorage = i;
    }
}

Následující příklad ukazuje, jak lze vytvořit pomocné nebo pracovní vlákno a použít k provádění zpracování paralelně s primárním vláknem. Další informace o multithreadingu naleznete v tématu Spravované vlákno.

public class Worker
{
    // This method is called when the thread is started.
    public void DoWork()
    {
        bool work = false;
        while (!_shouldStop)
        {
            work = !work; // simulate some work
        }
        Console.WriteLine("Worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Keyword volatile is used as a hint to the compiler that this data
    // member is accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    public static void Main()
    {
        // Create the worker thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("Main thread: starting worker thread...");

        // Loop until the worker thread activates.
        while (!workerThread.IsAlive)
            ;

        // Put the main thread to sleep for 500 milliseconds to
        // allow the worker thread to do some work.
        Thread.Sleep(500);

        // Request that the worker thread stop itself.
        workerObject.RequestStop();

        // Use the Thread.Join method to block the current thread
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("Main thread: worker thread has terminated.");
    }
    // Sample output:
    // Main thread: starting worker thread...
    // Worker thread: terminating gracefully.
    // Main thread: worker thread has terminated.
}

Po přidání modifikátoru k deklaraci volatile na místě vždy dosáhnete stejných výsledků (podobně jako výňatek zobrazený v předchozím kódu). Bez tohoto modifikátoru u člena _shouldStop je však chování nepředvídatelné. Metoda DoWork může optimalizovat přístup členů, což vede ke čtení zastaralých dat. Vzhledem k povaze vícevláknového programování je počet neaktuálních čtení nepředvídatelný. Různé běhy programu budou mít poněkud odlišné výsledky.

Specifikace jazyka C#

Další informace najdete ve specifikaci jazyka C#. Specifikace jazyka je konečným zdrojem syntaxe a použití jazyka C#.

Viz také