แนะนำให้ใช้ prevTask.Wait () กับ ContinueWith (จากไลบรารี Tasks) หรือไม่


88

เมื่อเร็ว ๆ นี้ฉันได้รับแจ้งว่าฉันใช้. ContinueWith for Tasks ของฉันอย่างไรไม่ใช่วิธีที่เหมาะสมในการใช้ ฉันยังไม่พบหลักฐานเกี่ยวกับเรื่องนี้บนอินเทอร์เน็ตดังนั้นฉันจะถามพวกคุณว่าคำตอบคืออะไร นี่คือตัวอย่างของวิธีที่ฉันใช้. ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

ตอนนี้ฉันรู้แล้วว่านี่เป็นตัวอย่างง่ายๆและมันจะทำงานเร็วมาก แต่แค่สมมติว่าแต่ละงานทำงานได้นานขึ้น ดังนั้นสิ่งที่ฉันได้รับแจ้งก็คือใน. ContinueWith คุณต้องพูดว่า prevTask.Wait (); มิฉะนั้นคุณสามารถทำงานก่อนที่งานก่อนหน้านี้จะเสร็จสิ้น เป็นไปได้หรือไม่? ฉันคิดว่างานที่สองและสามของฉันจะทำงานเมื่องานก่อนหน้านี้เสร็จสิ้นเท่านั้น

สิ่งที่ฉันบอกวิธีเขียนโค้ด:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

คำตอบ:


115

เอ๊ะ .... ฉันคิดว่าคำตอบในตอนนี้หายไปบางส่วน: จะเกิดอะไรขึ้นกับข้อยกเว้น?

เหตุผลเดียวที่คุณจะเรียกWaitอย่างต่อเนื่องคือการสังเกตข้อยกเว้นที่อาจเกิดขึ้นจากก่อนหน้าในความต่อเนื่องนั้นเอง การสังเกตเดียวกันนี้จะเกิดขึ้นหากคุณเข้าถึงResultในกรณีของไฟล์Task<T>และถ้าคุณเข้าถึงExceptionคุณสมบัติด้วยตนเอง ตรงไปตรงมาฉันจะไม่โทรWaitหรือเข้าถึงResultเพราะหากมีข้อยกเว้นคุณจะต้องจ่ายราคาของการเพิ่มขึ้นใหม่ซึ่งเป็นค่าใช้จ่ายที่ไม่จำเป็น แต่คุณสามารถตรวจสอบIsFaultedคุณสมบัติจากก่อนหน้านี้Taskได้ หรือคุณสามารถสร้างคดเคี้ยวเวิร์กโฟลว์โดยผูกมัดในตพี่น้องหลายว่ามีเพียงไฟไหม้อยู่บนพื้นฐานของความสำเร็จหรือความล้มเหลวและTaskContinuationOptions.OnlyOnRanToCompletionTaskContinuationOptions.OnlyOnFaulted

ตอนนี้คุณไม่จำเป็นต้องสังเกตข้อยกเว้นของก่อนหน้านี้ในความต่อเนื่อง แต่คุณอาจไม่ต้องการให้เวิร์กโฟลว์ของคุณก้าวไปข้างหน้าหากพูดว่า "ขั้นตอนที่ 1" ล้มเหลว ในกรณีนั้น: การระบุTaskContinuationOptions.NotOnFaultedการContinueWithโทรของคุณจะป้องกันไม่ให้ตรรกะความต่อเนื่องเริ่มทำงาน

โปรดทราบว่าหากความต่อเนื่องของคุณเองไม่ปฏิบัติตามข้อยกเว้นบุคคลที่กำลังรอให้ขั้นตอนการทำงานโดยรวมนี้เสร็จสมบูรณ์จะเป็นคนที่สังเกตเห็น ไม่ว่าพวกเขาจะWaitอยู่ที่Taskต้นน้ำหรือยึดติดกับความต่อเนื่องของตัวเองเพื่อให้ทราบเมื่อเสร็จสมบูรณ์ หากเป็นอย่างหลังความต่อเนื่องของพวกเขาจะต้องใช้ตรรกะการสังเกตดังกล่าวข้างต้น


2
ในที่สุดก็มีคนให้คำตอบที่ถูกต้อง @ Travyguy9 โปรดอ่านคำตอบของ @DrewMarsh และอ่านเพิ่มเติมเกี่ยวกับTaskContinuationOptions
แจสเปอร์

2
คำตอบที่ดีฉันกำลังมองหา"โปรดทราบว่าหากความต่อเนื่องของคุณไม่ปฏิบัติตามข้อยกเว้นบุคคลที่กำลังรอขั้นตอนการทำงานโดยรวมนี้ให้เสร็จสมบูรณ์จะเป็นผู้ที่สังเกตเห็น" คำถามหนึ่งข้อคือเมื่องานของคุณไม่ต้องรอใครคือพนักงานเสิร์ฟเริ่มต้น (ไม่สามารถหาคำตอบได้)
Thibault D.

20

คุณกำลังใช้มันอย่างถูกต้อง

สร้างความต่อเนื่องที่ดำเนินการแบบอะซิงโครนัสเมื่อภารกิจเป้าหมายเสร็จสิ้น

ที่มา: Task.ContinueWith Method (Action as MSDN)

การเรียกprevTask.Wait()ในการเรียกทุกครั้งTask.ContinueWithดูเหมือนจะเป็นวิธีที่แปลกในการทำซ้ำตรรกะที่ไม่จำเป็นนั่นคือการทำบางอย่างให้ "มั่นใจสุด ๆ " เพราะจริงๆแล้วคุณไม่เข้าใจว่าโค้ดบางส่วนทำอะไร เช่นเดียวกับการตรวจสอบค่าว่างเพียงเพื่อโยนไฟล์ArgumentNullExceptionว่ามันจะถูกโยนไปที่ใด

ดังนั้นไม่ใครก็ตามที่บอกคุณว่าผิดและอาจไม่เข้าใจว่าทำไมถึงTask.ContinueWithมีอยู่


16

ใครบอกคุณว่า?

การอ้างอิงMSDN :

สร้างความต่อเนื่องที่ดำเนินการแบบอะซิงโครนัสเมื่อภารกิจเป้าหมายเสร็จสิ้น

นอกจากนี้จุดประสงค์ของการดำเนินการต่อจะเป็นอย่างไรหากไม่รอให้งานก่อนหน้านี้เสร็จสมบูรณ์?

คุณสามารถทดสอบได้ด้วยตัวเอง:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5

จากMSDNบนTask.Continuewith

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

ฉันคิดว่าวิธีที่คุณคาดหวังว่าจะได้ผลในตัวอย่างแรกเป็นวิธีที่ถูกต้อง


2

คุณอาจต้องการพิจารณาใช้ Task.Run แทน Task.Factory.StartNew

โพสต์บล็อกของ Stephen Cleary และโพสต์ของ Stephen Toub ที่เขาอ้างอิงอธิบายความแตกต่าง นอกจากนี้ยังมีการอภิปรายในคำตอบนี้


4
ลดลงเนื่องจากไม่ตรงกับคำถามที่แท้จริง เป็นการเพิ่มคุณค่าบางอย่าง แต่ควรเป็นความคิดเห็น
Sinaesthetic

0

โดยการเข้าถึงTask.Resultคุณกำลังใช้ตรรกะที่คล้ายกันกับtask.wait


ใช่. เราสามารถหลีกเลี่ยงวิธี Wait () แต่ใช้ได้กับงานผลลัพธ์เท่านั้นเช่น Task <bool>
Alexander Ulmaskulov

ลดลงเนื่องจากไม่ตรงกับคำถามที่แท้จริง เป็นการเพิ่มคุณค่าบางอย่าง แต่ควรเป็นความคิดเห็น
Sinaesthetic

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