Dela via


volatile (C#-referens)

Använd nyckelordet volatile för att ange att ett fält kan ändras av flera trådar som körs samtidigt. Av prestandaskäl kan kompilatorn, körningssystemet och till och med maskinvaran ordna om läsningar och skrivningar till minnesplatser. Om du deklarerar ett fält som volatile undantas det från vissa typer av optimeringar. Det finns ingen garanti för en enda total ordning av flyktiga skrivningar som visas från alla körningstrå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 använder du 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 det skrivna värdet omedelbart visas för andra processorer.

C#-språkreferensen dokumenterar den senaste versionen av C#-språket. Den innehåller även inledande dokumentation för funktioner i offentliga förhandsversioner för den kommande språkversionen.

Dokumentationen identifierar alla funktioner som först introducerades i de tre senaste versionerna av språket eller i aktuella offentliga förhandsversioner.

Tips/Råd

Information om när en funktion först introducerades i C# finns i artikeln om språkversionshistoriken för C#.

Använd nyckelordet för volatile fält av följande typer:

  • Referenstyper.
  • Pekartyper (i en osäker kontext). Även om själva pekaren kan vara flyktig kan det objekt som den pekar på inte vara. Med andra ord kan du inte deklarera en "pekare till flyktig".
  • 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.

Du kan inte markera andra typer, inklusive double och long, eftersom volatile läsningar och skrivningar till fält av dessa typer inte kan garanteras vara atomiska. Om du vill skydda flertrådad åtkomst till dessa typer av fält använder du Interlocked klassmedlemmarna eller skyddar åtkomsten med hjälp av -instruktionen lock .

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.

Du kan bara använda nyckelordet volatile för fält i en class eller struct. Du kan inte deklarera lokala variabler som volatile.

Alternativ till flyktiga

I de flesta fall använder du 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 åtgärder är vanligtvis snabbare och ger starkare garantier än volatile.
  • lock instruktion: Ger ömsesidig uteslutning och minnesbarriärer. Använd den 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. Det förhindrar inte konkurrensförhållanden och ger inte ordergarantier för andra minnesåtgärder. Dessa begränsningar gör det olämpligt för de flesta samtidighetsscenarier.

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

När du lägger till volatile modifieraren i deklarationen av _shouldStopfår du alltid samma resultat (liknar utdraget 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 att inaktuella data läss. På grund av 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