Applicazioni più fluide a portata di mano.
Se ancora non utilizzate async e await, leggete questo articolo e poi vi chiederete come avete fatto a non utilizzarle fino ad ora.
L’utilizzo dei Thread e Task è utilissimo per le operazioni di elaborazione (calcolo) da rendere parallele sfruttando i molti core delle CPU odierne. Tuttavia quando si tratta di operare sull’I/O, è un po’ diverso. Quando viene effettua un'operazione di I/O sul thread primario, Windows, poichè si tratta di un’operazione di I/O, pone il thread primario in WAIT_STATE.
L’elaborazione asincrona risolverebbe il problema effettuando l’I/O mediante un altro thread. Ma questo non è sempre semplice da fare, perché va comunque sincronizzato il thread principale con quello di I/O.
A partire da Visual Studio 2012, esistono le keyword async / await che semplificano la vita, lasciando la complicazione al compilatore.
Un metodo marcato con async inizia la propria elaborazione in modo sincrono per poi diventare asincrono. Il confine tra la parte sincrona e quella asincrona è delimitato dalla keyword await (= attendere).
Dopo avere eseguito il metodo in await, l’esecuzione ritorna sincrona nel thread primario.
async e await non migliorano la velocità globale dell’applicazione, ma migliorano la velocità di risposta all’interazione dell’utente, dell’applicativo.
Approfondiamo il tema.
Cosa fa async? La keyword async nella firma di un metodo, ha unicamente due effetti: rende possibile utilizzare la keyword Await nel corpo del metodo, e cambia il modo in cui vengono gestiti i valori di ritorno (da Task a Task.Result).
async fa SOLO questo. Quindi non esegue il codice in modo asincrono, non esegue il codice in un threadpool e non ha poteri magici :-) L’esecuzione del codice di un metodo async è SINCRONA, esattamente come in ogni metodo.
Cosa fa await? Quando il codice eseguito nel corpo del metodo incontra await, allora le cose iniziano a diventare asincrone. await è un operatore unario, che prende come operando un awaitable. Un’awaitable, per semplificare, è l’astrazione di un’operazione asincrona.
L’awaitable viene eseguito in un thread separato. Il runtime si pone in attesa dell’awaitable “per un po’ di tempo”. Se il thread del metodo in await termina di lì a breve (entro il time slice corrente), il runtime continua ad eseguire il resto del codice del metodo async in modo sincrono, come un qualsiasi metodo. Se invece l’awaitable NON termina entro il time slice corrente, allora:
· Il metodo async restituisce immediatamente il controllo al chiamante e quindi il codice in risposta all’evento termina in modo anticipato, lasciando quindi “libero” il thread UI.
· Quando terminerà l’awaitable, verrà eseguita una nuova routine sincrona con il codice che segue l’await, sempre nel thread UI.
In definitiva, l'impiego di async / await fa sì che la nostra routine ad evento venga suddivisa automaticamente dal compilatore in 3 parti:
- Parte n.1: il codice che precede await viene eseguito nel thread primario (UI) in modo sincrono
- Parte n.2: il codice dell'await viene eseguito in un nuovo thread, in modo asincrono rispetto al thread primario (UI)
- Parte n.3: il codice che segue l’await viene eseguito nuovamente nel thread primario (UI) in modo sincrono
Buona sperimentazione!