CodeGym /Corsi /C# SELF /Annullamento delle operazioni asincrone

Annullamento delle operazioni asincrone

C# SELF
Livello 42 , Lezione 4
Disponibile

1. Introduzione

Quando avvii un'operazione asincrona (o lunga), l'utente (o un altro pezzo di codice) può improvvisamente decidere: "Aspetta! Non serve più! Fermati!". Per esempio, l'utente può interrompere il download di un file enorme, chiudere la finestra del programma o cambiare idea sulla ricerca in un grande database. Senza supporto per la cancellazione la tua applicazione può continuare a lavorare e consumare risorse inutilmente — non il massimo per l'esperienza utente né per il sistema.

Scenari tipici di cancellazione:

  • Annullamento del download di un file o dell'invio di dati.
  • Uscita rapida da un'elaborazione complessa su richiesta dell'utente.
  • Interruzione improvvisa di un compito lungo che non è più rilevante.

La cancellazione è il tuo ingrediente segreto per applicazioni reattive, rispettose e parsimoniose.

Come annullare operazioni asincrone in .NET?

In .NET per annullare compiti lunghi si usa il concetto del "token di cancellazione" (CancellationToken). È un oggetto speciale che viene passato a tutte le componenti di un'operazione. Se qualcuno richiede la cancellazione — il token segnala immediatamente a tutte le parti interessate. In pratica è come una bandierina rossa: chi la vede per primo si ferma.

In .NET questo meccanismo è realizzato con due classi chiave:

Importante: il token di cancellazione di per sé non interrompe l'esecuzione del codice, si limita a "segnalare" — poi l'applicazione decide come e quando reagire a quel segnale.

2. Creiamo un token di cancellazione e annulliamo un task

Vediamo come funziona con un esempio semplice (svilupperemo la nostra app console di studio).

Esempio: operazione asincrona semplice con cancellazione


using System;
using System.Threading;
using System.Threading.Tasks;

namespace DemoApp
{
    class Program
    {
        static async Task Main()
        {
            // Creiamo la source del token di cancellazione
            CancellationTokenSource cts = new CancellationTokenSource();

            // Avviamo il task asincrono
            Task longRunningTask = DoWorkAsync(cts.Token);

            Console.WriteLine("Premi un tasto per annullare l'operazione...");
            Console.ReadKey();

            // Richiediamo la cancellazione
            cts.Cancel();

            try
            {
                await longRunningTask;
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("L'operazione è stata annullata!");
            }
        }

        // Metodo asincrono che supporta la cancellazione
        static async Task DoWorkAsync(CancellationToken cancellationToken)
        {
            for (int i = 0; i < 10; i++)
            {
                // Controlliamo il segnale di cancellazione
                cancellationToken.ThrowIfCancellationRequested();

                Console.WriteLine($"Esecuzione passo {i + 1}/10...");
                await Task.Delay(1000); // Ritardo di 1 secondo
            }

            Console.WriteLine("Operazione completata con successo!");
        }
    }
}

Come funziona?

- Creiamo CancellationTokenSource (cts), da cui otteniamo il token (cts.Token).
- Passiamo questo token alla nostra operazione asincrona.
- Dentro DoWorkAsync() controlliamo regolarmente il token con il metodo ThrowIfCancellationRequested(). Se l'utente richiede la cancellazione — il metodo lancerà l'eccezione OperationCanceledException e il task si interromperà.
- In Main() aspettiamo la pressione di un tasto e chiamiamo cts.Cancel() per "segnalare" la necessità di fermare l'operazione.

Se non controllerai cancellationToken.IsCancellationRequested o non chiamerai ThrowIfCancellationRequested(), il tuo task continuerà a girare come se nulla fosse — il token è solo una bandierina informativa.

3. CancellationToken: come è fatto? E un po' di magia

Il token di cancellazione è un oggetto che puoi passare facilmente tra metodi e task. Questo dà molta flessibilità:

  • Lo stesso token può essere usato in più operazioni asincrone e sincrone.
  • Puoi organizzare una "cancellazione di gruppo" per tutti i task che condividono il token di una singola CancellationTokenSource.
  • Il token è non invasivo: anche se lo ignori, il codice continuerà a funzionare come prima.

Gestire la cancellazione: dove e come controllare il token?

Controllare se è stato alzato il "segnale di cancellazione" va fatto dove ha senso: in loop, ad ogni passo di una lunga elaborazione, al passaggio tra fasi ecc.


// In qualsiasi punto di controllo
if (cancellationToken.IsCancellationRequested)
{
    Console.WriteLine("Operazione annullata! Esco...");
    return;
}

// Oppure così (breve e con lancio di eccezione)
cancellationToken.ThrowIfCancellationRequested();

Di solito si usa ThrowIfCancellationRequested() — lancia un'eccezione speciale che può essere catturata nel codice chiamante.

4. Metodi asincroni della libreria standard

Molte classi e metodi .NET (soprattutto asincroni) supportano CancellationToken direttamente "out of the box". Usali per fermare le operazioni in modo "corretto".

Ecco un esempio con lettura asincrona da file:


using System.IO;
using System.Threading;
using System.Threading.Tasks;

class FileDemo
{
    public static async Task ReadFileWithCancelAsync(string filePath, CancellationToken cancellationToken)
    {
        using FileStream stream = File.OpenRead(filePath);
        byte[] buffer = new byte[4096];

        int bytesRead;
        while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
        {
            // Elaboriamo i dati...
            // Se il token di cancellazione è stato richiesto, ReadAsync lancerà da solo OperationCanceledException
        }
    }
}

Per saperne di più su FileStream.ReadAsync e CancellationToken consulta la documentazione ufficiale.

5. Dettagli utili

Timeout — è anche cancellazione!

Puoi annullare automaticamente operazioni dopo un tempo prefissato. Per questo il CancellationTokenSource può essere "programmato":


// Creare CancellationTokenSource con timeout (per esempio, 5 secondi)
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

Dopo 5 secondi il token sarà automaticamente "alzato" e tutte le operazioni con esso si fermeranno al prossimo controllo. Molto comodo se non vuoi aspettare all'infinito.

Cosa succede quando si annulla?

Quando chiami Cancel() sulla source del token, tutti i metodi che usano quel token e ne controllano lo stato vengono informati. Ma se il tuo codice non controlla il token — l'annullamento non avverrà.

Errore tipico: dimenticare di passare il token in tutti i metodi asincroni e lunghi. Allora una parte dell'operazione verrà annullata e un'altra continuerà a funzionare come se niente fosse.

Visualizzazione: come fluisce l'annullamento

sequenceDiagram
    participant Main as Thread principale
    participant CTS as CancellationTokenSource
    participant Task as Task asincrono

    Main->>CTS: crea CTS, ottiene token
    Main->>Task: passa il token al task asincrono
    Note over Task: Il task controlla periodicamente il token
    Main->>CTS: chiama Cancel()
    CTS-->>Task: il token riceve lo stato "cancellato"
    Task-->>Main: lancia OperationCanceledException
    Main->>Main: cattura l'eccezione e termina

Dove si applica la cancellazione delle operazioni asincrone?

  • Download e richieste asincrone al server: puoi annullare con connessione instabile o se l'utente si stufa.
  • Calcoli pesanti: puoi fermarli per timeout o su richiesta dell'utente.
  • Operazioni di rete, lavoro con file, elaborazione di grandi collezioni in background.

Con questo la panoramica sull'annullamento delle operazioni asincrone è completa — la tua applicazione sarà non solo veloce, ma anche attenta!

6. Consigli ed errori tipici

Non dimenticare di passare il token di cancellazione a tutti i metodi e alle chiamate che supportano cancellation. Se lo perdi da qualche parte — l'operazione potrebbe "bloccarsi" e non fermarsi.

Controlla regolarmente il token — specialmente in loop lunghi, elaborazioni di file o download di grandi dimensioni. Usa IsCancellationRequested o ThrowIfCancellationRequested().

Non cercare di "terminare forzatamente" thread o task dall'esterno: il token di cancellazione è una richiesta di fermarsi, non una mazza per colpire il thread.

Le funzioni della libreria standard come ReadAsync, Delay, HttpClient.SendAsync e molte altre già supportano la cancellazione tramite token. Usale!

Quando gestisci la cancellazione cattura specificamente OperationCanceledException — è l'eccezione che indica una cancellazione corretta su richiesta.

1
Sondaggio/quiz
Operazioni asincrone con i file, livello 42, lezione 4
Non disponibile
Operazioni asincrone con i file
Vantaggi del lavoro asincrono con i file
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION