อะไรคือความแตกต่างระหว่างการคืนค่าว่างเปล่าและการส่งคืนงาน?


128

ในการมองในหลาย ๆ ตัวอย่าง C # Async CTP ผมเห็นบาง async หน้าที่ว่าการกลับมาvoidและคนอื่น ๆ Taskที่ส่งกลับไม่ใช่ทั่วไป ฉันเห็นว่าเหตุใดการส่งคืน a Task<MyType>จึงมีประโยชน์ในการส่งคืนข้อมูลไปยังผู้โทรเมื่อการดำเนินการ async เสร็จสิ้น แต่ฟังก์ชันที่ฉันเห็นว่ามีประเภทการTaskส่งคืนไม่ส่งคืนข้อมูลใด ๆ ทำไมไม่กลับvoidล่ะ

คำตอบ:


214

คำตอบของ SLaks and Killercam นั้นดี ฉันคิดว่าฉันจะเพิ่มบริบทอีกเล็กน้อย

คำถามแรกของคุณเป็นหลักเกี่ยวกับวิธีการที่สามารถทำเครื่องหมายasyncได้

วิธีที่ทำเครื่องหมายว่าasyncสามารถส่งคืนvoidได้TaskหรือTask<T>. อะไรคือความแตกต่างระหว่างพวกเขา?

คุณTask<T>สามารถรอเมธอด async ที่ส่งคืนได้และเมื่องานเสร็จสมบูรณ์จะได้รับ T

คุณTaskสามารถรอเมธอด async ที่ส่งคืนได้และเมื่องานเสร็จสมบูรณ์ความต่อเนื่องของงานจะถูกกำหนดให้รัน

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

คำถามที่สองของคุณในความคิดเห็นเกี่ยวกับสิ่งที่สามารถแก้ไขได้await:

มีวิธีการใดบ้างที่สามารถแก้ไขได้await? วิธีการคืนค่าโมฆะสามารถแก้ไขได้awaitหรือไม่?

ไม่ได้ไม่สามารถรอวิธีการคืนค่าเป็นโมฆะได้ คอมไพลเลอร์แปลawait M()เป็น call to M().GetAwaiter()ซึ่งGetAwaiterอาจเป็นวิธีการอินสแตนซ์หรือวิธีการขยาย มูลค่าที่รอคอยจะต้องเป็นมูลค่าที่คุณรอได้ เห็นได้ชัดว่าวิธีการคืนค่าเป็นโมฆะไม่ก่อให้เกิดมูลค่าที่คุณจะได้รับ

Task- วิธีการคืนค่าสามารถสร้างมูลค่าที่รอคอยได้ เราคาดหวังว่าบุคคลที่สามจะต้องการสร้างการนำTaskอ็อบเจ็กต์ที่มีลักษณะเหมือนของตนเองไปใช้งานและคุณจะสามารถรอได้ แต่คุณจะไม่ได้รับอนุญาตที่จะประกาศasyncวิธีการที่ผลตอบแทนอะไร แต่void, หรือTaskTask<T>

(อัปเดต: ประโยคสุดท้ายของฉันอาจมีการปลอมแปลงโดย C # เวอร์ชันอนาคตมีข้อเสนอให้อนุญาตประเภทการส่งคืนอื่นที่ไม่ใช่ประเภทงานสำหรับวิธีการ async)

(อัปเดต: คุณลักษณะที่กล่าวถึงข้างต้นทำให้อยู่ใน C # 7)


7
+1 ฉันคิดว่าสิ่งเดียวที่ขาดหายไปคือความแตกต่างในการปฏิบัติตามข้อยกเว้นในวิธีการ async ที่คืนค่าเป็นโมฆะ
João Angelo

10
@JamesCadd: สมมติว่างานอะซิงโครนัสบางงานมีข้อยกเว้น ใครจับได้? รหัสที่เริ่มต้นงานอะซิงโครนัสไม่ได้อยู่บนสแต็กอีกต่อไป - อาจไม่ได้อยู่ในเธรดเดียวกัน- และข้อยกเว้นจะถือว่าบล็อก catch / สุดท้ายทั้งหมดอยู่บนสแต็ก แล้วคุณจะทำอย่างไร? เราจัดเก็บข้อมูลข้อยกเว้นไว้ในงานเพื่อให้คุณสามารถตรวจสอบได้ในภายหลัง แต่ถ้าวิธีนี้เป็นโมฆะกลับมาแสดงว่าไม่มีงานสำหรับรหัสผู้ใช้ เราจัดการกับสถานการณ์นั้นได้อย่างไรเป็นเรื่องของการโต้เถียงและฉันจำสิ่งที่เราตัดสินใจไม่ได้ในตอนนี้
Eric Lippert

8
ฉันถามคำถามนี้ของ Stephen Toub ที่ BUILD จริงๆ ใน. NET 4.0 ที่ไม่ถูกตรวจจับข้อยกเว้นที่ไม่สามารถจัดการได้ใน Tasks จะทำให้กระบวนการล้มเหลวในที่สุดเมื่อ TPL ตรวจพบว่าไม่มีการปฏิบัติตาม ใน 4.5 พวกเขาได้เปลี่ยนพฤติกรรมเริ่มต้นเพื่อให้ยังคงมีการรายงานข้อยกเว้นที่ไม่ถูกสังเกตผ่านเหตุการณ์ TaskScheduler :: UnobservedTaskException แต่จะไม่ขัดข้องในกระบวนการอีกต่อไป หากคุณต้องการพฤติกรรม 4.0 แบบเดิมคุณสามารถเลือกกลับมาใช้ได้โดยใช้ <runtime> <ThrowUnobservedTaskExceptions enable = "true" /> </runtime> เป็นไปได้มากว่าการเปลี่ยนแปลงเกิดขึ้นอย่างแม่นยำเพื่อรองรับการใช้ไฟและการลืมสำหรับวิธีการ async ที่เป็นโมฆะ
Drew Marsh

4
async voidวิธีการเพิ่มข้อยกเว้นของพวกเขาเกี่ยวกับSynchronizationContextที่ใช้งานอยู่ในขณะที่พวกเขาเริ่มดำเนินการ ซึ่งคล้ายกับลักษณะการทำงานของตัวจัดการเหตุการณ์ (ซิงโครนัส) @DrewMarsh: การUnobservedTaskExceptionตั้งค่าและรันไทม์ใช้กับเมธอดasync Task "fire and forget" เท่านั้นไม่ใช่async voidวิธีการ
Stephen Cleary

1
ลิงก์อ้างอิงสำหรับข้อมูลการจัดการข้อยกเว้น async: blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx#11
Luke Puplett

23

ในกรณีที่ผู้โทรต้องการรองานหรือเพิ่มความต่อเนื่อง

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


ฉันคิดว่ามันเป็นไปได้ที่จะรอเมธอดที่ส่งคืนประเภทที่เป็นโมฆะเช่นกัน - คุณช่วยอธิบายอีกนิดได้ไหม
James Cadd

1
ไม่คุณทำไม่ได้ หากวิธีการคืนค่าvoidคุณจะไม่มีทางได้รับงานที่สร้างขึ้น (อันที่จริงฉันไม่แน่ใจว่ามันสร้างได้หรือTask
เปล่า

18

วิธีการส่งคืนTaskและTask<T>สามารถประกอบได้ - หมายความว่าคุณสามารถทำได้awaitภายในasyncวิธีการ

asyncวิธีการส่งคืนvoidไม่สามารถประกอบได้ แต่มีคุณสมบัติที่สำคัญอีกสองอย่าง:

  1. สามารถใช้เป็นตัวจัดการเหตุการณ์
  2. ซึ่งแสดงถึงการดำเนินการแบบอะซิงโครนัส "ระดับบนสุด"

จุดที่สองมีความสำคัญเมื่อคุณต้องจัดการกับบริบทที่รักษาจำนวนการดำเนินการแบบอะซิงโครนัสที่โดดเด่น

บริบท ASP.NET เป็นบริบทดังกล่าว หากคุณใช้Taskวิธีการasync โดยไม่ต้องรอจากvoidวิธีการasync คำขอ ASP.NET จะเสร็จเร็วเกินไป

บริบทอื่นคือAsyncContextฉันเขียนสำหรับการทดสอบหน่วย (มีให้ที่นี่ ) - AsyncContext.Runวิธีการติดตามจำนวนการดำเนินการที่ค้างอยู่และส่งคืนเมื่อเป็นศูนย์


12

Type Task<T>เป็นประเภทworkhorse ของ Task Parallel Library (TPL) ซึ่งแสดงถึงแนวคิดของ "งาน / งานบางอย่างที่กำลังจะสร้างผลลัพธ์เป็นประเภทTในอนาคต" แนวคิดของ "งานที่จะเสร็จสมบูรณ์ในอนาคต แต่ไม่ส่งคืนผลลัพธ์" จะแสดงโดยประเภทงานที่ไม่ใช่ทั่วไป

แม่นยำว่าผลลัพธ์ของประเภทTจะถูกสร้างขึ้นอย่างไรและรายละเอียดการนำไปใช้งานของงานใดงานหนึ่ง งานอาจถูกส่งไปยังกระบวนการอื่นบนเครื่องโลคัลไปยังเธรดอื่นเป็นต้นโดยทั่วไปแล้วงาน TPL จะถูกส่งไปยังเธรดของผู้ปฏิบัติงานจากเธรดพูลในกระบวนการปัจจุบัน แต่รายละเอียดการใช้งานนั้นไม่ได้เป็นพื้นฐานของTask<T>ประเภท ค่อนข้างเป็นTask<T>ตัวแทนของการดำเนินการที่มีความหน่วงแฝงสูงซึ่งก่อให้เกิดไฟล์T.

ตามความคิดเห็นของคุณด้านบน:

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


แก้ไข: ฉันควรอ้างอิงบทความของ Eric Lippert ในเดือนตุลาคม 2554 MSDN Magazine เพราะนี่เป็นความช่วยเหลือที่ดีสำหรับฉันในการทำความเข้าใจสิ่งนี้ตั้งแต่แรก

สำหรับการโหลดกลุ่มข้อมูลข่าวสารมากขึ้นและ Whitepages ดูที่นี่

ฉันหวังว่านี่จะเป็นประโยชน์บ้าง

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