Perché abbiamo bisogno di più di un'istruzione `await` in un metodo C#?

Perché abbiamo bisogno di più di un'istruzione `await` in un metodo C#?

Hai bisogno di tanto attesa nel tuo codice, vuoi (a) attendere il completamento dell'esecuzione del metodo asincrono chiamato. Quando chiami un metodo asincrono, (a un certo punto!) restituirà un'attività (incompleta o completata), che tecnicamente è una promessa da quel metodo che a un certo punto completerà il suo lavoro.

Ad esempio _bookRepository.InsertAsync(...) promette che inserirà l'elemento nel repository e ti avviserà tramite l'attività restituita quando è successo. Ora sta a te, il chiamante, se vuoi aspettare usando await, o non ti interessa se e quando questo lavoro è terminato (spara e dimentica), quindi non usi await e continua ad eseguire il resto di il codice del chiamante.

Quindi è assolutamente valido rimuovere le parole chiave await quasi ovunque, ma c'è una probabilità molto alta altererà il flusso del programma e potrebbe causare effetti collaterali (spiegazione nella sezione successiva).

Ci sono diversi malintesi qui:

  • La chiamata a un metodo asincrono non rende il codice asincrono. Il codice chiamato verrà eseguito in modo sincrono fino a quando il metodo chiamato o qualsiasi chiamata di metodo figlio restituisce un'attività, che non è già completata . L'attesa del completamento dell'attività rende la chiamata e la continuazione completamente sincrone!
  • Seguendo il punto precedente, le chiamate al metodo asincrono non creano o allocano thread di per sé. Spetta al chiamato codice per "caricare lateralmente" il suo lavoro in un modo o nell'altro.
  • L'uso della parola chiave await "metterà" il codice rimanente dopo la parola chiave in una continuazione che verrà eseguita in modo asincrono, ma non dice nulla sui thread o ne crea necessariamente uno! Si deve immaginare che la continuazione venga messa in coda e verrà eseguita a un certo punto da a filo.

Senza ulteriori indagini sul progetto che hai citato, molto probabilmente _bookRepository.InsertAsync(...) i metodi non sono "sicuri in parallelo", altrimenti await Task.WhenAll(insert1, insert2) si sarebbe potuto usare il formato. Inoltre, il mancato utilizzo di await per gli inserimenti potrebbe causare effetti collaterali, ad esempio multi threading come race condition (lettura dello stato prima che la scrittura sia stata completata).

MODIFICA: Puoi trovare molto materiale di lettura utile su docs.microsoft.com, come questo:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming- modello

Suggerisco di leggerli più volte e di creare app di prova perché l'argomento è più complesso di quanto sembri e pieno di piccoli dettagli facili da interpretare erroneamente.


await attenderà che l'operazione non venga eseguita. Quindi hai 2 operazioni asincrone, ecco perché devi usare await.

Un await per ogni operazione asincrona (metodo).

Quindi, hai 3 metodi asincroni. Puoi chiamarlo senza attendere, ma andrà in crash. Quando lo chiami senza await, inizierà a essere eseguito in un altro thread e il thread in cui è in esecuzione SeedAsync non attenderà l'esecuzione di InsertAsync. Inizierà contemporaneamente il secondo InsertAsync

Quindi, nel tuo caso, puoi inserire valori senza attendere. Funzionerà. Ma in casi comuni è meglio usare await. Perché spesso l'ordine delle operazioni è importante. await consente di controllare l'ordine delle operazioni

A volte è necessario eseguire alcune attività e quindi attendere tutte. Quindi puoi utilizzare Task.WhenAll(t1,t2,t3)


Se rimuovi gli ultimi due await , il tuo codice diventerà così:

public async Task SeedAsync(DataSeedContext context)
{
    if (await _bookRepository.GetCountAsync() == 0)
    {
        _bookRepository.InsertAsync(new Book("Title1"));
        _bookRepository.InsertAsync(new Book("Title2"));
    }
}

Immediatamente riceverai due avvisi:

Il messaggio dell'avviso è sufficientemente descrittivo. Senza questi due await :

  1. I due InsertAsync le attività verranno eseguite contemporaneamente. Ciò potrebbe causare il danneggiamento dello stato nel caso in cui il InsertAsync manipola lo stato condiviso senza sincronizzazione.
  2. I chiamanti del SeedAsync il metodo riceverà un Task che ne segnalerà il completamento prima che sia effettivamente completato.
  3. Eventuali eccezioni non gestite che potrebbero verificarsi durante l'esecuzione dei due InsertAsync le attività rimarranno inosservate.