Chờ tất cả so với khi nào


Câu trả lời:


502

Task.WaitAll chặn luồng hiện tại cho đến khi mọi thứ đã hoàn thành.

Task.WhenAlltrả về một nhiệm vụ đại diện cho hành động chờ đợi cho đến khi mọi thứ đã hoàn thành.

Điều đó có nghĩa là từ phương thức async, bạn có thể sử dụng:

await Task.WhenAll(tasks);

... Điều đó có nghĩa là phương pháp của bạn sẽ tiếp tục khi mọi thứ hoàn thành, nhưng bạn sẽ không buộc một sợi chỉ để treo xung quanh cho đến lúc đó.


2
Sau khi đọc nhiều, rõ ràng async không liên quan gì đến chủ đề blog.stephencleary.com/2013/11/there-is-no-thread.html
Vince Panuccio

7
@Vince: Tôi nghĩ rằng "không có gì để làm với các chủ đề" là một cách nói quá và điều quan trọng là phải hiểu cách các hoạt động async tương tác với các chủ đề.
Jon Skeet

6
@KevinBui: Không, không nên chặn nó - nó sẽ chờ nhiệm vụ được trả về WhenAll, nhưng điều đó không giống như chặn chuỗi.
Jon Skeet

1
@JonSkeet Có lẽ sự phân biệt chính xác giữa hai người đó là quá tinh tế đối với tôi. Bạn có thể chỉ cho tôi (và có thể, phần còn lại của chúng tôi) tại một số tài liệu tham khảo sẽ làm rõ sự khác biệt?
CatShoes

124
@CatShoes: Không thực sự - Tôi đã giải thích nó cũng như tôi có thể. Tôi đoán rằng tôi có thể đưa ra một sự tương tự - nó giống như sự khác biệt giữa việc đặt mua và sau đó đứng cạnh cửa chờ nó đến, so với đặt mua, làm những thứ khác và sau đó mở cửa khi chuyển phát nhanh đến ...
Jon Xiên

50

Trong khi câu trả lời của JonSkeet giải thích sự khác biệt theo cách điển hình xuất sắc, có một sự khác biệt khác: xử lý ngoại lệ .

Task.WaitAllném một AggregateExceptionkhi bất kỳ nhiệm vụ nào ném và bạn có thể kiểm tra tất cả các ngoại lệ ném. Các awaittrong await Task.WhenAllunwraps AggregateExceptionvà 'lợi nhuận' chỉ là ngoại lệ đầu tiên.

Khi chương trình dưới đây thực hiện với await Task.WhenAll(taskArray)đầu ra như sau.

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.

Khi chương trình dưới đây được thực hiện với Task.WaitAll(taskArray)đầu ra như sau.

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.

Chương trình:

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
sự khác biệt thực tế lớn nhất là xử lý ngoại lệ. Có thật không? Bởi vì đó thực sự không phải là sự khác biệt thực tế lớn nhất. Sự khác biệt thực tế lớn nhất là một là không đồng bộ và không chặn trong khi khác đang chặn. Đây là nhiều quan trọng hơn cách nó xử lý ngoại lệ.
Liam

5
Cảm ơn đã chỉ ra điều này. Giải thích này rất hữu ích trong kịch bản tôi hiện đang làm việc. Có lẽ không phải là "sự khác biệt thực tế lớn nhất", nhưng chắc chắn là một cuộc gọi tốt.
Urk

Việc xử lý ngoại lệ là sự khác biệt thực tế lớn nhất có thể được áp dụng nhiều hơn để so sánh giữa await t1; await t2; await t3;vsawait Task.WhenAll(t1,t2,t3);
frostshoxx

1
Không phải hành vi ngoại lệ này mâu thuẫn với các tài liệu ở đây ( docs.microsoft.com/en-us/dotnet/api/ mẹo ) "Nếu bất kỳ tác vụ được cung cấp nào hoàn thành ở trạng thái bị lỗi, tác vụ được trả lại cũng sẽ hoàn thành ở trạng thái Lỗi. , trong đó các ngoại lệ của nó sẽ chứa tập hợp các tập hợp ngoại lệ chưa được mã hóa từ mỗi tác vụ được cung cấp. "
Dasith Wijes

1
Tôi coi đây là một tạo tác của await, không phải là một sự khác biệt giữa hai phương pháp. Cả hai tuyên truyền một AggregateException, hoặc ném trực tiếp hoặc thông qua một tài sản ( Task.Exceptiontài sản).
Theodor Zoulias

20

Như một ví dụ về sự khác biệt - nếu bạn có một nhiệm vụ, thì hãy thực hiện điều gì đó với luồng UI (ví dụ: một tác vụ đại diện cho hoạt hình trong Storyboard) nếu bạn Task.WaitAll()thì luồng UI bị chặn và UI không bao giờ được cập nhật. nếu bạn sử dụng await Task.WhenAll()thì luồng UI không bị chặn và UI sẽ được cập nhật.


7

Họ làm gì:

  • Bên trong cả hai đều làm điều tương tự.

Có gì khác biệt:

  • Wait ALL là một cuộc gọi chặn
  • Khi tất cả - không - mã sẽ tiếp tục thực thi

Sử dụng khi nào:

  • Chờ tất cả khi không thể tiếp tục mà không có kết quả
  • WhenAll khi những gì chỉ để được thông báo, không bị chặn

1
@MartinRhodes Nhưng nếu bạn không chờ đợi nó ngay lập tức, nhưng tiếp tục với một số công việc khác và sau đó chờ đợi thì sao? Bạn không có khả năng đó WaitAllnhư tôi hiểu.
Jeppe

@Jeppe Bạn sẽ không khác biệt cuộc gọi Task.WaitAll sau khi bạn đã thực hiện một số công việc khác của bạn? Ý tôi là, thay vì gọi nó ngay sau khi bắt đầu nhiệm vụ của bạn.
PL
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.