WaitAll vs WhenAll


Відповіді:


504

Task.WaitAll блокує поточну нитку, поки все не завершиться.

Task.WhenAllповертає завдання, яке представляє дію очікування, поки все не завершиться.

Це означає, що з методу асинхронізації можна використовувати:

await Task.WhenAll(tasks);

... це означає, що ваш метод продовжиться, коли все буде завершено, але ви не зв’яжете нитку, щоб просто зависати до цього часу.



7
@Vince: Я думаю, що "нічого спільного з потоками" не є завищенням, і важливо зрозуміти, як операції асинхронізації взаємодіють з потоками.
Джон Скіт

6
@KevinBui: Ні, він не повинен його блокувати - він буде чекати завдання, яке повертається WhenAll, але це не те саме, що блокувати нитку.
Джон Скіт

1
@JonSkeet Можливо, точне розмежування цих двох для мене занадто тонке. Чи можете ви вказати мені (а можливо, і решті нас) на якусь посилання, яка дозволить зрозуміти різницю?
CatShoes

125
@CatShoes: Не дуже - я пояснив це так добре, як вже можу. Я думаю, я міг би дати аналогію - це як різниця між замовленням вильоту та стоячи біля дверей, очікуючи приїзду, проти замовлення вильоту, робити інші речі, а потім відчиняти двері, коли кур'єр приїжджає ...
Джон Skeet

51

Хоча відповідь JonSkeet пояснює різницю типово відмінним способом, є ще одна відмінність: обробка виключень .

Task.WaitAllкидає AggregateExceptionколи будь-яке завдання кидає, і ви можете вивчити всі викинуті винятки. awaitУ await Task.WhenAllрозгортає AggregateExceptionі «повертається» тільки перше виняток.

Коли програма нижче виконується з await Task.WhenAll(taskArray)висновком, виглядає наступним чином.

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.

Коли програма, виконана нижче, виконується Task.WaitAll(taskArray)таким чином.

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.

Програма:

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
Найбільша практична різниця - обробка виключень. Дійсно? Тому що це насправді не найбільша практична різниця. Найбільша практична відмінність - це асинхронізація та неблокування, де блокується інше. Це набагато важливіше, ніж поводження з винятками.
Ліам

5
Дякуємо, що вказали на це. Це пояснення було корисним у сценарії, над яким я зараз працюю. Можливо, не "найбільша практична різниця", але, безумовно, хороший виклик.
Урк

Обробка винятків, яка є найбільшою практичною різницею, може бути більш застосовною для порівняння між await t1; await t2; await t3;vsawait Task.WhenAll(t1,t2,t3);
frostshoxx

1
Чи не поведінка цього винятку не суперечить документам тут ( docs.microsoft.com/en-us/dotnet/api/… ) "Якщо будь-яке із поставлених завдань завершиться у несправному стані, повернене завдання також буде виконане у несправному стані , де його винятки будуть містити агрегацію набору розгорнутих винятків з кожної із заданих задач. "
Dasith Wijes

1
Я вважаю це артефактом await, а не різницею між двома методами. Обидва розповсюджують AggregateException, або кидаючи безпосередньо, або через майно ( Task.Exceptionвластивість).
Теодор Зуліяс

20

Як приклад різниці - якщо у вас є завдання, робиться щось з потоком інтерфейсу (наприклад, завдання, яке представляє анімацію на дошці розкадрування), якщо ви Task.WaitAll()потоку користувальницького інтерфейсу заблоковані і користувальницький інтерфейс ніколи не оновлюється. якщо ви використовуєте, await Task.WhenAll()то потік інтерфейсу не блокується, і інтерфейс буде оновлений.


7

Що вони роблять:

  • Всередині обидва роблять те саме.

Яка різниця:

  • WaitAll - це дзвінок, що блокує
  • Коли все - не - код буде продовжувати виконувати

Використовуйте, коли:

  • Зачекайте всі, коли не можна продовжувати, не маючи результату
  • WhenAll коли те, що просто повідомляється, не блокується

1
@MartinRhodes Але що робити, якщо ви його не чекаєте негайно, а продовжуєте якусь іншу роботу, а потім чекаєте її? У вас немає такої можливості, WaitAllяк я це розумію.
Jeppe

@Jeppe Ви б не просто відрізнили дзвінок Task.WaitAll після того, як зробили якусь іншу роботу? Я маю на увазі, замість того, щоб викликати це відразу після початку своїх завдань.
PL
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.