Learning SynchronizationContext, async e await

La programmazione asincrona è una forma di programmazione parallela che consente di eseguire attività separate dal thread dell'applicazione principale e quindi notifica al thread quando la sua esecuzione è terminata. Asynchrony ti aiuta a eseguire attività senza la necessità di bloccare il flusso di esecuzione o la reattività della tua applicazione.

Microsoft ha fornito supporto per la programmazione parallela in .Net Framework per sfruttare i vantaggi dei sistemi multi core. Puoi sfruttare l'asincronia per migliorare le prestazioni e la reattività della tua applicazione.

Essenzialmente, ci sono due possibili tipi di operazioni in un'applicazione. Questi includono operazioni associate al calcolo e operazioni di I / O. Le operazioni legate al calcolo sono quelle in cui il calcolo può essere eseguito su un thread separato in modo che il thread principale possa continuare con la sua esecuzione. Al contrario, le operazioni di I / O bound sono quelle in cui vengono eseguite esternamente e quindi non hanno bisogno di bloccare il thread corrente mentre l'I / O è in corso.

Contesto di sincronizzazione e contesto di esecuzione

A ogni thread è associato un contesto, noto anche come contesto "corrente", e questi contesti possono essere condivisi tra i thread. ExecutionContext contiene i metadati rilevanti dell'ambiente o del contesto corrente in cui il programma è in esecuzione. Il SynchronizationContext rappresenta un'astrazione: denota la posizione in cui viene eseguito il codice dell'applicazione.

Un SynchronizationContext consente di accodare un'attività in un altro contesto. Si noti che ogni thread può avere il proprio SynchronizatonContext. La classe SynchronizationContext è stata aggiunta di recente allo spazio dei nomi System.Threading e facilita la comunicazione tra i thread. Puoi leggere ulteriori informazioni su SynchronizationContext ed ExecutionContext qui.

Un'immersione profonda in Async e Await

I tre modelli di programmazione asincrona includono quanto segue:

  1. Modello di programmazione asincrona (APM)
  2. Pattern asincrono basato su eventi (EAP)
  3. Pattern asincrono basato su attività (TAP)

L'ultimo, il consigliato e anche il più elegante di tutti è il TAP.

Tieni presente che puoi contrassegnare un metodo utilizzando la parola chiave "async" che restituisce void, Task o Task. Si noti che quando si verifica un'eccezione all'interno di un metodo asincrono che ha un tipo restituito di Task o Task, i dettagli dell'eccezione vengono archiviati all'interno dell'istanza di Task.

Al contrario, quando si verifica un'eccezione all'interno di un metodo asincrono che ha un tipo restituito void, i dettagli dell'eccezione vengono memorizzati all'interno del SynchronizationContext che era attivo al momento in cui è stato chiamato il metodo asincrono. In sostanza, non è possibile gestire le eccezioni sollevate all'interno di un metodo asincrono con un tipo restituito void utilizzando gestori di eccezioni scritti all'interno del metodo asincrono. A causa della diversa semantica di elaborazione e di gestione degli errori, è consigliabile evitare metodi asincroni con tipi restituiti void a meno che non vi sia una ragione sufficiente per utilizzarli.

Quando si utilizza la parola chiave "await" all'interno di un metodo asincrono, il metodo viene suddiviso all'interno di una macchina a stati. Si noti che la parola chiave "await" acquisisce il SynchronizationContext corrente e non appena l'attività che è stata attesa utilizzando la parola chiave "await" è stata completata, la macchina a stati viene ripresa e viene riavviata l'esecuzione del codice nel metodo chiamante - anche noto come continuazione. Se l'esecuzione del codice atteso utilizzando la parola chiave "await" è stata completata nel momento in cui viene rilevato il punto di sospensione, il metodo asincrono (il metodo che è stato contrassegnato come "async") viene eseguito in modo sincrono. Se l'esecuzione del codice atteso non è completa, al codice atteso viene allegato un delegato di continuazione.

Puoi sfruttare i metodi asincroni che restituiscono void per creare gestori di eventi asincroni. Il metodo Main non può essere contrassegnato con la parola chiave "async" poiché è il punto di ingresso dell'applicazione: un metodo Main "async" terminerà nel momento in cui viene chiamato. La parola chiave "await" informa il compilatore che il metodo può avere un punto di sospensione e ripresa. Per inciso, è possibile utilizzare la parola chiave "await" solo su un metodo che è stato contrassegnato come asincrono utilizzando la parola chiave "async".

Un metodo asincrono quando viene chiamato, viene eseguito in modo sincrono sul thread corrente indipendentemente dal tipo restituito del metodo. Quando contrassegni un metodo come asincrono utilizzando la parola chiave "async", informi il compilatore che il metodo può essere suddiviso in più attività: alcune di queste attività possono essere eseguite in modo asincrono. Inoltre, l'inclusione della parola chiave "async" in un metodo non mette in coda la chiamata del metodo come parte del pool di thread. L'asincronia (cioè se un metodo avrebbe un comportamento asincrono) dipende in realtà dal punto di sospensione che hai menzionato nel tuo metodo usando la parola chiave "await". Se non includi la parola chiave "await" in un metodo asincrono, l'intero metodo verrà eseguito in modo sincrono.