Dela via


volatile (C#-referens)

Nyckelordet volatile anger att ett fält kan ändras av flera trådar som körs samtidigt. Kompilatorn, körningssystemet och till och med maskinvaran kan ordna om läsningar och skrivningar till minnesplatser av prestandaskäl. Fält som deklareras volatile undantas från vissa typer av optimeringar. Det finns ingen garanti för en enda total ordning av flyktiga skrivningar som ses från alla exekveringstrådar. Mer information finns i Volatile klassen .

Försiktighet

Nyckelordet volatile är ofta missförstådd och missbrukas i flertrådad programmering. I de flesta scenarier bör du använda säkrare och mer tillförlitliga alternativ i stället för volatile. Moderna .NET ger bättre samtidighetsverktyg som klassen Interlocked, satsen lock eller synkroniseringsprimitive på högre nivå. Dessa alternativ ger tydligare semantik och starkare garantier än volatile. Överväg att bara använda volatile i sällsynta, avancerade scenarier där du helt förstår dess begränsningar och har verifierat att det är rätt lösning.

Anmärkning

I ett system med flera processorer garanterar inte en flyktig läsåtgärd att det senaste värdet skrivs till den minnesplatsen av någon processor. På samma sätt garanterar inte en flyktig skrivåtgärd att värdet som skrivs omedelbart visas för andra processorer.

Nyckelordet volatile kan tillämpas på fält av följande typer:

  • Referenstyper.
  • Pekartyper (i en osäker kontext). Observera att även om själva pekaren kan vara flyktig kan det objekt som den pekar på inte göra det. Med andra ord kan du inte deklarera en "pekare till volatil".
  • Enkla typer som sbyte, byte, short, ushort, int, uint, char, float, och bool.
  • En enum typ med någon av följande bastyper: byte, sbyte, short, ushort, int, eller uint.
  • Generiska typparametrar som är kända för att vara referenstyper.
  • IntPtr och UIntPtr.

Andra typer, inklusive double och long, kan inte markeras volatile eftersom läsningar och skrivningar till fält av dessa typer inte kan garanteras vara atomiska. Om du vill skydda åtkomst med flera trådar till dessa typer av fält använder du Interlocked-klassmedlemmarna eller skyddar åtkomsten med lock-instruktionen.

För de flesta flertrådade scenarier, även med typer som stöds, föredrar du att använda Interlocked åtgärder, lock instruktioner eller andra synkroniseringspri primitiver i stället för volatile. Dessa alternativ är mindre benägna för subtila samtidighetsbuggar.

Nyckelordet volatile kan endast tillämpas på fält i en class eller struct. Lokala variabler kan inte deklareras volatile.

Alternativ till flyktiga

I de flesta fall bör du använda något av dessa säkrare alternativ i stället för volatile:

  • Interlocked åtgärder: Tillhandahålla atomiska åtgärder för numeriska typer och referenstilldelningar. Dessa är vanligtvis snabbare och ger starkare garantier än volatile.
  • lock instruktion: Ger ömsesidig uteslutning och minnesbarriärer. Används för att skydda större kritiska avsnitt.
  • Volatile klass: Ger explicita flyktiga läs- och skrivåtgärder med tydligare semantik än nyckelordet volatile .
  • Synkronisering primitiver på högre nivå: till exempel ReaderWriterLockSlim, Semaphoreeller samtidiga samlingar från System.Collections.Concurrent.

Nyckelordet volatile ger inte atomitet för andra åtgärder än tilldelning, förhindrar inte konkurrensvillkor och ger inte ordergarantier för andra minnesåtgärder. Dessa begränsningar gör det olämpligt för de flesta samtidighetsscenarier.

Exempel

I följande exempel visas hur du deklarerar en offentlig fältvariabel som volatile.

class VolatileTest
{
    public volatile int sharedStorage;

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

I följande exempel visas hur en hjälptråd eller arbetstråd kan skapas och användas för att utföra bearbetning parallellt med den primära trådens. Mer information om multitrådning finns i Hanterad trådning.

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.
}

Med volatile-modifieraren på plats i deklarationen av _shouldStop får du alltid samma resultat (liknande det utdrag som visas i föregående kod). Men utan den modifieraren på _shouldStop medlemmen är beteendet oförutsägbart. Metoden DoWork kan optimera medlemsåtkomsten, vilket resulterar i läsning av inaktuella data. På grund av karaktären hos multitrådad programmering är antalet inaktuella läsningar oförutsägbart. Olika körningar av programmet ger något olika resultat.

Språkspecifikation för C#

Mer information finns i C#-språkspecifikationen. Språkspecifikationen är den slutgiltiga källan för C#-syntax och -användning.

Se även