Le 13 sconosciute (o quasi): Cronache di funzionalità C# che pochi usano (e forse un motivo c’è) – Puntata 3

Reading Time: 2 minutes

volatile – La keyword che ipotizza sempre il peggio

La paranoia multithread

Benvenuti nel mondo di volatile. Questo non è un keyword da tutti i giorni. Se state pensando di usarlo per la vostra applicazione CRUD standard, per favore, tornate a scrivere new() e ToList().

volatile è per i paranoici, per coloro che non si fidano minimamente del compilatore o della cache della CPU quando si tratta di operazioni multithreading. Dici al compilatore: “Ehi! Non fidarti! Ogni volta che leggi questa variabile, vai direttamente nella memoria principale. Non osare usare la cache locale, perché chissà, magari l’altro thread l’ha cambiata un nanosecondo fa!”

È l’equivalente del tennista che mette in discussione ogni singola chiamata dell’arbitro, anche se il colpo era palesemente out. Non si fida di nessuno. E in un ambiente multithread, a volte, non fidarsi è l’unica strategia.

“In the end, you should always do the right thing, even if it’s hard.” – Roger Federer (leggenda del tennis).

In C#, fare la cosa giusta (usare i meccanismi di sincronizzazione di .NET) è spesso più difficile che usare volatile. Ma a volte, quando la posta in gioco è alta e non puoi fidarti della CPU, volatile è il tuo blocco difensivo. Attenzione: volatile non è un sostituto per i lock. È come dire al tuo attaccante di fare pressing: non è una difesa completa, ma aiuta.

La memoria sotto sorveglianza

Quando si tratta di un flag che può essere letto da un thread e scritto da un altro, e non volete usare un lock (perché rallenterebbe troppo, o è un contesto specifico):

C#

public class Stopper
{
    // Il keyword volatile obbliga la CPU a ricaricare questo campo dalla 
    // memoria ogni volta che viene letto, e a scaricarlo subito dopo averlo scritto.
    // Senza di esso, un altro thread potrebbe leggere un valore in cache obsoleto.
    private volatile bool _stopRequested = false;

    public void RequestStop()
    {
        // Scrive immediatamente in memoria
        _stopRequested = true; 
    }

    public void WorkerMethod()
    {
        // Legge sempre dalla memoria principale per vedere se c'è una richiesta
        while (!_stopRequested) 
        {
            // Lavora sodo come un operaio
            Thread.Sleep(50); 
        }
        Console.WriteLine("Worker interrotto.");
    }
}

Ricorda, volatile non garantisce operazioni atomiche complesse. Per quello, avrai bisogno di armi più pesanti (come lock o Interlocked). Ma per un semplice bool o int, è come un rapido “sguardo furtivo” alla memoria principale.

Se questo articolo ti è piaciuto condividilo
Torna in alto