รองานที่เสร็จสมบูรณ์เช่นเดียวกับงานผลลัพธ์?


117

ฉันกำลังอ่าน " Concurrency in C # Cookbook " โดย Stephen Cleary และสังเกตเห็นเทคนิคต่อไปนี้:

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTaskคือเรียกร้องให้httpclient.GetStringAsyncและกำลังดำเนินการtimeoutTaskTask.Delay

ในกรณีที่ไม่หมดเวลาแสดงว่าdownloadTaskเป็นที่เรียบร้อยแล้ว เหตุใดจึงจำเป็นต้องรอเป็นครั้งที่สองแทนที่จะกลับมาdownloadTask.Resultเนื่องจากงานเสร็จเรียบร้อยแล้ว


3
มีบริบทบางอย่างที่ขาดหายไปที่นี่และเว้นแต่ผู้คนจะเข้าถึงหนังสือได้ง่ายคุณจะต้องรวมไว้ด้วย คืออะไรdownloadTaskและtimeoutTask? พวกเขาทำอะไร?
Mike Perrenoud

7
ฉันไม่เห็นการตรวจสอบจริงสำหรับความสำเร็จที่นี่ งานอาจผิดพลาดได้เป็นอย่างดีและในกรณีนี้พฤติกรรมจะแตกต่างกัน ( AggregateExceptionโดยResultเทียบกับข้อยกเว้นแรกExceptionDispatchInfoด้วยawait) กล่าวถึงรายละเอียดเพิ่มเติมใน "Task Exception Handling ใน. NET 4.5" ของ Stephen Toub: blogs.msdn.com/b/pfxteam/archive/2011/09/28/… )
Kirill Shlenskiy

คุณควรให้คำตอบนี้ @KirillShlenskiy
Carsten

@MichaelPerrenoud คุณพูดถูกขอบคุณที่สังเกตเห็นฉันจะแก้ไขคำถาม
julio.g

คำตอบ:


160

มีคำตอบ / ความคิดเห็นที่ดีอยู่แล้วที่นี่ แต่เพียงเพื่อตีระฆังใน ...

มีสองเหตุผลที่ฉันชอบawaitมากกว่าResult(หรือWait) ประการแรกคือการจัดการข้อผิดพลาดแตกต่างกัน awaitไม่รวมข้อยกเว้นในAggregateExceptionไฟล์. ตามหลักการแล้วรหัสอะซิงโครนัสไม่ควรต้องจัดการAggregateExceptionเลยเว้นแต่จะต้องการเป็นพิเศษ

เหตุผลที่สองนั้นละเอียดกว่าเล็กน้อย ขณะที่ผมอธิบายในบล็อกของฉัน (และในหนังสือ) Result/ Waitอาจทำให้เกิดการหยุดชะงักและสามารถทำให้เกิดการหยุดชะงักแม้ลึกซึ้งยิ่งขึ้นเมื่อใช้ในasyncวิธีการ ดังนั้นเมื่อฉันอ่านโค้ดและฉันเห็นResultหรือWaitนั่นคือธงเตือนทันที Result/ Waitถูกต้องเท่านั้นถ้าคุณแน่ใจจริงๆว่างานจะเสร็จสมบูรณ์แล้ว สิ่งนี้ไม่เพียง แต่มองเห็นได้ยาก (ในรหัสโลกแห่งความจริง) แต่การเปลี่ยนแปลงโค้ดยังเปราะบางกว่าอีกด้วย

นั่นไม่ได้หมายความว่าResult/ ไม่Waitควรใช้ ฉันปฏิบัติตามหลักเกณฑ์เหล่านี้ในรหัสของฉันเอง:

  1. awaitรหัสตรงกันในโปรแกรมประยุกต์สามารถใช้
  2. รหัสยูทิลิตี้แบบอะซิงโครนัส (ในไลบรารี) สามารถใช้ได้เป็นครั้งคราวResult/ Waitหากรหัสเรียกใช้ การใช้งานดังกล่าวน่าจะมีความคิดเห็น
  3. รหัสงานคู่ขนานสามารถใช้ResultและWait.

โปรดทราบว่า (1) เป็นกรณีทั่วไปดังนั้นฉันจึงมีแนวโน้มที่จะใช้awaitทุกที่และถือว่ากรณีอื่น ๆ เป็นข้อยกเว้นของกฎทั่วไป


เราพบการชะงักงันโดยใช้ 'ผลลัพธ์' แทนที่จะเป็น 'รอ' ในโครงการของเรา ส่วนที่ยุ่งคือไม่มีข้อผิดพลาดในการคอมไพล์และโค้ดของคุณจะไม่สม่ำเสมอหลังจากนั้นสักครู่
Ahmad Mousavi

@Stephen คุณช่วยอธิบายฉันได้ไหมว่าทำไม "ตามหลักการแล้วรหัสอะซิงโครนัสไม่ควรมีการจัดการกับ AggregateException เลยเว้นแต่จะต้องการเป็นพิเศษ"
vcRobe

4
@vcRobe เนื่องจากawaitป้องกันการAggregateExceptionห่อหุ้ม AggregateExceptionได้รับการออกแบบมาสำหรับการเขียนโปรแกรมแบบขนานไม่ใช่การเขียนโปรแกรมแบบอะซิงโครนัส
Stephen Cleary

2
> "การรอจะถูกต้องก็ต่อเมื่อคุณแน่ใจจริงๆว่างานเสร็จเรียบร้อยแล้ว" .... แล้วทำไมถึงเรียกว่ารอ?
Ryan The Leach

4
@RyanTheLeach: จุดประสงค์เดิมWaitคือการเข้าร่วมกับอินสแตนซ์Dynamic Task Parallelism Taskการใช้เพื่อรอTaskอินสแตนซ์อะซิงโครนัสนั้นอันตราย Microsoft พิจารณาแนะนำประเภท "Promise" ใหม่ แต่เลือกที่จะใช้ที่มีอยู่Taskแทน ข้อเสียของการนำTaskประเภทที่มีอยู่กลับมาใช้ซ้ำสำหรับงานอะซิงโครนัสคือคุณต้องใช้ API หลายตัวที่ไม่ควรใช้ในโค้ดอะซิงโครนัส
Stephen Cleary

12

สิ่งนี้สมเหตุสมผลถ้าtimeoutTaskเป็นผลิตภัณฑ์Task.Delayซึ่งฉันเชื่อว่ามันคืออะไรในหนังสือ

Task.WhenAnyส่งกลับTask<Task>โดยที่งานภายในเป็นหนึ่งในงานที่คุณส่งผ่านเป็นอาร์กิวเมนต์ สามารถเขียนซ้ำได้ดังนี้:

Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)  
  return null;  
return downloadTask.Result; 

ในทั้งสองกรณีเพราะdownloadTaskได้เสร็จสิ้นแล้วมีความแตกต่างน้อยมากระหว่างและreturn await downloadTask return downloadTask.ResultในภายหลังจะโยนAggregateExceptionซึ่งครอบคลุมข้อยกเว้นดั้งเดิมใด ๆ ตามที่ @KirillShlenskiy ชี้ไว้ในความคิดเห็น เดิมจะโยนข้อยกเว้นเดิมใหม่

ไม่ว่าในกรณีใดก็ตามที่คุณจัดการกับข้อยกเว้นคุณควรตรวจสอบAggregateExceptionและข้อยกเว้นด้านในอย่างไรก็ตามเพื่อหาสาเหตุของข้อผิดพลาด

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