WaitAll vs WhenAll


Risposte:


504

Task.WaitAll blocca il thread corrente fino al completamento di tutto.

Task.WhenAllrestituisce un'attività che rappresenta l'azione dell'attesa fino al completamento di tutto.

Ciò significa che da un metodo asincrono è possibile utilizzare:

await Task.WhenAll(tasks);

... il che significa che il tuo metodo continuerà quando tutto sarà completato, ma non legherai un thread per restare in attesa fino a quel momento.


2
Dopo molte letture, è chiaro che async non ha nulla a che fare con le discussioni blog.stephencleary.com/2013/11/there-is-no-thread.html
Vince Panuccio,

7
@Vince: penso che "niente a che fare con i thread" sia un'esagerazione, ed è importante capire come le operazioni asincrone interagiscono con i thread.
Jon Skeet,

6
@KevinBui: No, non dovrebbe bloccarlo : attenderà l'attività restituita WhenAll, ma non è lo stesso che bloccare il thread.
Jon Skeet,

1
@JonSkeet Forse la distinzione precisa tra quei due è troppo sottile per me. Potete indicarmi (e possibilmente, il resto di noi) alcuni riferimenti che chiariranno la differenza?
CatShoes,

125
@CatShoes: Non proprio - l'ho spiegato come posso già. Immagino di poter fare un'analogia - è come la differenza tra ordinare un asporto e poi stare in piedi vicino alla porta in attesa che arrivi, vs ordinare un asporto, fare altre cose e quindi aprire la porta quando arriva il corriere ...
Jon Skeet

51

Mentre la risposta di JonSkeet spiega la differenza in un modo tipicamente eccellente, c'è un'altra differenza: la gestione delle eccezioni .

Task.WaitAllgenera un AggregateExceptionmomento in cui viene eseguita una delle attività e puoi esaminare tutte le eccezioni generate. In awaitin await Task.WhenAllriavvolge AggregateExceptione "restituisce" solo la prima eccezione.

Quando il programma seguente viene eseguito con await Task.WhenAll(taskArray)l'output è il seguente.

19/11/2016 12:18:37 AM: Task 1 started
19/11/2016 12:18:37 AM: Task 3 started
19/11/2016 12:18:37 AM: Task 2 started
Caught Exception in Main at 19/11/2016 12:18:40 AM: Task 1 throwing at 19/11/2016 12:18:38 AM
Done.

Quando il programma seguente viene eseguito con Task.WaitAll(taskArray)l'output è il seguente.

19/11/2016 12:19:29 AM: Task 1 started
19/11/2016 12:19:29 AM: Task 2 started
19/11/2016 12:19:29 AM: Task 3 started
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 1 throwing at 19/11/2016 12:19:30 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 2 throwing at 19/11/2016 12:19:31 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 3 throwing at 19/11/2016 12:19:32 AM
Done.

Il programma:

class MyAmazingProgram
{
    public class CustomException : Exception
    {
        public CustomException(String message) : base(message)
        { }
    }

    static void WaitAndThrow(int id, int waitInMs)
    {
        Console.WriteLine($"{DateTime.UtcNow}: Task {id} started");

        Thread.Sleep(waitInMs);
        throw new CustomException($"Task {id} throwing at {DateTime.UtcNow}");
    }

    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await MyAmazingMethodAsync();
        }).Wait();

    }

    static async Task MyAmazingMethodAsync()
    {
        try
        {
            Task[] taskArray = { Task.Factory.StartNew(() => WaitAndThrow(1, 1000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(2, 2000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(3, 3000)) };

            Task.WaitAll(taskArray);
            //await Task.WhenAll(taskArray);
            Console.WriteLine("This isn't going to happen");
        }
        catch (AggregateException ex)
        {
            foreach (var inner in ex.InnerExceptions)
            {
                Console.WriteLine($"Caught AggregateException in Main at {DateTime.UtcNow}: " + inner.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught Exception in Main at {DateTime.UtcNow}: " + ex.Message);
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
}

13
la più grande differenza pratica è la gestione delle eccezioni. Veramente? Perché quella non è la differenza pratica più grande. La più grande differenza pratica è che uno è asincrono e non blocca mentre l'altro blocca. Questo è molto più importante di come gestisce le eccezioni.
Liam,

5
Grazie per averlo segnalato. Questa spiegazione è stata utile nello scenario in cui sto attualmente lavorando. Forse non è la "più grande differenza pratica", ma sicuramente una buona chiamata.
Urk,

La gestione delle eccezioni essendo la più grande differenza pratica potrebbe essere più applicabile al confronto tra await t1; await t2; await t3;vsawait Task.WhenAll(t1,t2,t3);
frostshoxx il

1
Questo comportamento di eccezione non contraddice i documenti qui ( docs.microsoft.com/en-us/dotnet/api/… ) "Se una delle attività fornite viene completata in uno stato di errore, l'attività restituita verrà completata anche in uno stato di errore , in cui le sue eccezioni conterranno l'aggregazione dell'insieme di eccezioni non scartate da ciascuna delle attività fornite. "
Dasith Wijes

1
Considero questo un artefatto await, non una differenza tra i due metodi. Entrambi propagano un AggregateException, lanciando direttamente o attraverso una proprietà (la Task.Exceptionproprietà).
Theodor Zoulias,

20

Come esempio della differenza: se hai un'attività, fa qualcosa con il thread dell'interfaccia utente (ad esempio un'attività che rappresenta un'animazione in uno storyboard) se Task.WaitAll()il thread dell'interfaccia utente viene bloccato e l'interfaccia utente non viene mai aggiornata. se lo usi, await Task.WhenAll()il thread dell'interfaccia utente non viene bloccato e l'interfaccia utente verrà aggiornata.


7

Cosa fanno:

  • Internamente entrambi fanno la stessa cosa.

Qual è la differenza:

  • WaitAll è una chiamata bloccante
  • WhenAll - not - code continuerà l'esecuzione

Utilizzare quale quando:

  • Attendi Tutto quando non puoi continuare senza avere il risultato
  • Quando Tutto ciò che deve essere notificato, non bloccato

1
@MartinRhodes Ma cosa succede se non lo aspetti immediatamente, ma continui con qualche altro lavoro e poi lo aspetti? Non hai questa possibilità con WaitAllcome ho capito.
Jeppe

@Jeppe Non differiresti la chiamata solo Task.WaitAll dopo aver fatto qualche altro lavoro? Voglio dire, invece di chiamarlo subito dopo aver iniziato i tuoi compiti.
PL
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.