WaitAll vs WhenAll


335

ความแตกต่างระหว่างTask.WaitAll()และTask.WhenAll()จาก Async CTP คืออะไร? คุณสามารถให้รหัสตัวอย่างเพื่อแสดงกรณีการใช้ที่แตกต่างกันได้หรือไม่?

คำตอบ:


504

Task.WaitAll บล็อกเธรดปัจจุบันจนกว่าทุกอย่างจะเสร็จสมบูรณ์

Task.WhenAllส่งคืนงานซึ่งแสดงถึงการกระทำของการรอจนกว่าทุกอย่างจะเสร็จสมบูรณ์

ซึ่งหมายความว่าจากวิธีการซิงค์คุณสามารถใช้:

await Task.WhenAll(tasks);

... ซึ่งหมายความว่าวิธีการของคุณจะดำเนินต่อไปเมื่อทุกอย่างเสร็จสมบูรณ์ แต่คุณจะไม่ผูกด้ายเพื่อรอจนกว่าจะถึงเวลานั้น


2
หลังจากที่ได้อ่านมากเป็นที่ชัดเจนว่า async มีอะไรจะทำอย่างไรกับหัวข้อblog.stephencleary.com/2013/11/there-is-no-thread.html
วินซ์ Panuccio

7
@Vince: ฉันคิดว่า "การไม่ทำอะไรกับเธรด" เป็นการกล่าวเกินจริงและเป็นสิ่งสำคัญที่จะต้องเข้าใจว่าการดำเนินการของ async ทำงานร่วมกับเธรดอย่างไร
Jon Skeet

6
@KevinBui: ไม่ไม่ควรปิดกั้น - มันจะรองานที่ส่งคืนโดยWhenAllแต่นั่นไม่เหมือนกับการบล็อกเธรด
Jon Skeet

1
@ JonSkeet บางทีความแตกต่างที่ชัดเจนระหว่างสองสิ่งนั้นอาจจะบอบบางเกินไปสำหรับฉัน คุณช่วยชี้ฉัน (และอาจเป็นพวกเราที่เหลือ) ในการอ้างอิงที่จะทำให้ความแตกต่างชัดเจน?
CatShoes

125
@ CatShoes: ไม่จริง - ฉันอธิบายได้ดีเท่าที่จะทำได้แล้ว ฉันคิดว่าฉันสามารถให้การเปรียบเทียบ - มันเหมือนกับความแตกต่างระหว่างการสั่งซื้ออาหารกลับบ้านแล้วยืนอยู่ข้างประตูรอให้มันมาถึงเทียบกับการสั่งซื้ออาหารกลับบ้านทำสิ่งอื่น ๆ แล้วเปิดประตูเมื่อผู้ส่งเอกสารมาถึง ...
Jon Skeet

51

ในขณะที่คำตอบ JonSkeet อธิบายความแตกต่างในทางที่ดีมักจะมีความแตกต่างอื่น: การจัดการข้อยกเว้น

Task.WaitAllโยนAggregateExceptionเมื่องานใด ๆ พ่นและคุณสามารถตรวจสอบข้อยกเว้นโยนทั้งหมด awaitในawait Task.WhenAllunwraps 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
ความแตกต่างในทางปฏิบัติที่ใหญ่ที่สุดคือการจัดการข้อยกเว้น จริงๆ? เพราะนั่นไม่ใช่ความแตกต่างในทางปฏิบัติที่ยิ่งใหญ่ที่สุดจริงๆ ความแตกต่างในทางปฏิบัติที่ใหญ่ที่สุดคือสิ่งหนึ่งคือ async และไม่ใช่การปิดกั้นที่อื่น ๆ คือการปิดกั้น นี้เป็นมากสิ่งที่สำคัญกว่าวิธีการที่จะจัดการกับข้อยกเว้น
เลียม

5
ขอบคุณที่ชี้นำสิ่งนี้ คำอธิบายนี้มีประโยชน์ในสถานการณ์ที่ฉันกำลังทำงานอยู่ บางทีอาจไม่ใช่ "ความแตกต่างในทางปฏิบัติที่ยิ่งใหญ่ที่สุด" แต่เป็นสิ่งที่ดี
Urk

การจัดการข้อยกเว้นเป็นความแตกต่างในทางปฏิบัติที่ใหญ่ที่สุดอาจมีผลกับการเปรียบเทียบระหว่าง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ทรัพย์สิน)
Theodor Zoulias

20

เป็นตัวอย่างของความแตกต่าง - ถ้าคุณมีงานทำสิ่งที่มีเธรด UI (เช่นงานที่แสดงถึงภาพเคลื่อนไหวในกระดานเรื่องราว) ถ้าคุณTask.WaitAll()แล้วเธรด UI จะถูกบล็อกและ UI จะไม่ถูกอัปเดต หากคุณใช้await Task.WhenAll()เธรด UI จะไม่ถูกบล็อกและ UI จะได้รับการอัปเดต


7

พวกเขาทำอะไร:

  • ภายในทั้งสองทำสิ่งเดียวกัน

ความแตกต่างคืออะไร:

  • WaitAllเป็นการบล็อกการโทร
  • เมื่อรหัสทั้งหมดไม่ทำงานจะยังคงทำงานต่อไป

ใช้เมื่อใด:

  • WaitAllเมื่อไม่สามารถดำเนินการต่อโดยไม่มีผลลัพธ์
  • WhenAllเมื่อสิ่งที่เพียงเพื่อให้ได้รับการแจ้งไม่ได้ปิดกั้น

1
@MartinRhodes แต่ถ้าคุณไม่รอมันทันที แต่ทำต่อไปกับงานอื่นแล้วรออีก คุณไม่มีโอกาสเป็นไปได้WaitAllเพราะฉันเข้าใจ
Jeppe

@Jeppe คุณจะไม่โทรต่างไปTask.WaitAll หลังจากที่คุณทำงานอื่น ๆ ใช่มั้ย ฉันหมายถึงแทนที่จะเรียกมันทันทีหลังจากเริ่มงานของคุณ
PL
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.