Task.Result เหมือนกับ. GetAwaiter.GetResult () หรือไม่


328

ฉันเพิ่งอ่านโค้ดที่ใช้วิธีการแบบอะซิงก์จำนวนมาก แต่บางครั้งก็จำเป็นต้องเรียกใช้งานแบบซิงโครนัส รหัสทำ:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

เป็นเช่นนี้หรือไม่

Foo foo = GetFooAsync(...).Result;

8
จากเอกสารของGetResult: "ประเภทนี้และสมาชิกมีไว้สำหรับใช้งานโดยคอมไพเลอร์" บุคคลอื่นไม่ควรใช้งาน
spender

32
สิ่งนี้เรียกว่า "sync over async" และหากคุณไม่ทราบว่าการใช้งานนั้นเป็นความคิดที่ไม่ดีจริงๆ สามารถหยุดชะงักได้ทันทีในหลายกรณี (เช่นasync/ awaitวิธีการใน MVC)
Marc Gravell


14
ในโลกแห่งความเป็นจริงเรามีคอนสตรัคเตอร์เรามีอินเทอร์เฟซ "ไม่รอคอย" ที่เราต้องนำไปใช้ ฉันยินดีที่จะใช้สิ่งที่ใช้งานได้โดยไม่ต้องสงสัยว่าทำไม "อันตราย", "ไม่ควรใช้" หรือ "หลีกเลี่ยงค่าใช้จ่ายทั้งหมด" ทุกครั้งที่ฉันต้องยุ่งกับ async กลายเป็นปวดหัว
Larry

คำตอบ:


173

ค่อนข้างมาก หนึ่งความแตกต่างเล็ก ๆ แม้ว่า: ถ้าTaskล้มเหลวGetResult()ก็จะโยนข้อยกเว้นเกิดโดยตรงในขณะที่จะโยนTask.Result AggregateExceptionอย่างไรก็ตามประเด็นของการใช้งานอย่างใดอย่างหนึ่งเมื่อมันคือasyncอะไร? ตัวเลือกที่ดี 100x awaitคือการใช้งาน

GetResult()นอกจากนี้คุณยังจะไม่ได้หมายถึงการใช้งาน มันมีไว้สำหรับคอมไพเลอร์ใช้เท่านั้นไม่ใช่สำหรับคุณ แต่ถ้าคุณไม่ต้องการความรำคาญAggregateExceptionให้ใช้มัน


27
@JayBazuzi ไม่ว่ากรอบการทดสอบหน่วยของคุณรองรับการทดสอบหน่วย async ซึ่งฉันคิดว่ารุ่นใหม่ล่าสุดของกรอบการทำงานส่วนใหญ่ทำ
svick

15
@JayBazuzi: MSTest, xUnit และ NUnit async Taskทดสอบหน่วยสนับสนุนทั้งหมดและมีเวลาสักครู่แล้ว
Stephen Cleary

18
ผลักกลับไปที่ 100x - มันแย่กว่าที่จะใช้เป็น 1000x หากคุณกำลังปรับรหัสเก่าและการใช้งานที่ต้องรอจะต้องเขียนใหม่
ติด

13
@AlexZhukovskiy: ผมไม่เห็นด้วย
Stephen Cleary

15
The 100x better option is to use await.ฉันเกลียดข้อความเช่นนี้หากฉันสามารถตบawaitหน้าฉันได้ แต่เมื่อฉันพยายามรับรหัส async เพื่อทำงานกับรหัสที่ไม่ใช่ async เหมือนสิ่งที่เกิดขึ้นกับฉันบ่อยครั้งมากใน Xamarin ฉันจบลงด้วยการใช้สิ่งต่าง ๆContinueWithมากมายเพื่อที่จะไม่หยุดยั้ง UI แก้ไข: ฉันรู้ว่านี่เก่า แต่ก็ไม่ได้ช่วยลดความคับข้องใจในการหาคำตอบที่ระบุสิ่งนี้โดยไม่มีทางเลือกอื่นสำหรับสถานการณ์ที่คุณไม่สามารถใช้งานawaitได้
โทมัสเอฟ.

147

Task.GetAwaiter().GetResult()เป็นที่ต้องการมากกว่าTask.Waitและเพราะมันแพร่กระจายข้อยกเว้นมากกว่าการตัดพวกเขาในTask.Result AggregateExceptionอย่างไรก็ตามทั้งสามวิธีทำให้เกิดปัญหาการหยุดชะงักและความอดอยากของพูลเธรด async/awaitพวกเขาควรจะหลีกเลี่ยงในความโปรดปรานของ

เครื่องหมายคำพูดด้านล่างอธิบายสาเหตุTask.WaitและTask.Resultไม่เพียง แต่ประกอบด้วยพฤติกรรมการแพร่กระจายข้อยกเว้นของTask.GetAwaiter().GetResult()(เนื่องจาก "แถบความเข้ากันได้สูงมาก")

ดังที่ฉันได้กล่าวไปแล้วก่อนหน้านี้เรามีแถบความเข้ากันได้สูงมากและดังนั้นเราจึงหลีกเลี่ยงการเปลี่ยนแปลงที่ไม่ดี เช่นนี้Task.Waitจะยังคงพฤติกรรมดั้งเดิมของการห่อเสมอ แต่คุณอาจพบว่าตัวเองอยู่ในสถานการณ์ขั้นสูงบางอย่างที่คุณต้องการพฤติกรรมคล้ายกับการปิดกั้นการซิงโครการจ้างงานโดยแต่ที่คุณต้องการให้ยกเว้นเดิมยังไม่ได้เปิดขยายพันธุ์มากกว่ามันถูกห่อหุ้มอยู่ในTask.Wait AggregateExceptionเพื่อให้บรรลุเป้าหมายนั้นคุณสามารถกำหนดเป้าหมายพนักงานรอคอยของภารกิจได้โดยตรง เมื่อคุณเขียน“ await task;” คอมไพเลอร์แปลว่าเป็นการใช้Task.GetAwaiter()วิธีซึ่งจะส่งคืนอินสแตนซ์ที่มีGetResult()วิธีการ เมื่อใช้กับงานที่มีข้อบกพร่องGetResult()จะเผยแพร่ข้อยกเว้นดั้งเดิม (นี่คือวิธีที่“ await task;” ได้รับพฤติกรรม) คุณสามารถใช้“task.GetAwaiter().GetResult()” หากคุณต้องการเรียกใช้ตรรกะการเผยแพร่นี้โดยตรง

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

GetResult” จริงหมายถึง“ ตรวจสอบงานเพื่อหาข้อผิดพลาด”

โดยทั่วไปแล้วฉันพยายามทำให้ดีที่สุดเพื่อหลีกเลี่ยงการบล็อกแบบอะซิงโครนัส อย่างไรก็ตามมีบางสถานการณ์ที่ฉันละเมิดหลักเกณฑ์ดังกล่าว ในสภาพที่หายากเหล่านั้นวิธีการที่ต้องการของฉันคือเพราะมันจะเก็บรักษาข้อยกเว้นงานแทนการตัดพวกเขาในGetAwaiter().GetResult()AggregateException

http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html


3
ดังนั้นโดยทั่วไปจะเทียบเท่ากับTask.GetAwaiter().GetResult() await taskฉันคิดว่าตัวเลือกแรกจะใช้เมื่อวิธีการไม่สามารถทำเครื่องหมายด้วยasync(นวกรรมิกเช่น) ถูกต้องหรือไม่ ถ้าใช่มันจะชนกับคำตอบยอดนิยม @ It'sNotALie
OlegI

5
@OlegI: Task.GetAwaiter().GetResult()เทียบเท่ากับTask.WaitและTask.Result(ซึ่งทั้งสามจะบล็อกแบบซิงโครนัสและมีศักยภาพสำหรับการหยุดชะงัก) แต่Task.GetAwaiter().GetResult()มีพฤติกรรมการแพร่กระจายข้อยกเว้นของการรองาน
Nitin Agarwal

คุณไม่สามารถหลีกเลี่ยงการหยุดชะงักในสถานการณ์นี้ด้วย (Task) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz

3
@DanielLorenz: ดูคำพูดต่อไปนี้: "การใช้ ConfigureAwait (false) เพื่อหลีกเลี่ยงการหยุดชะงักเป็นวิธีปฏิบัติที่อันตรายคุณจะต้องใช้ ConfigureAwait (false) สำหรับทุก ๆ การรอคอยในการปิด transitive ของวิธีการทั้งหมดที่เรียกใช้โดยรหัสการปิดกั้น - และโค้ดของบุคคลที่สองการใช้ ConfigureAwait (false) เพื่อหลีกเลี่ยงการหยุดชะงักเป็นวิธีที่ดีที่สุดในการแฮ็ค) ... ทางออกที่ดีกว่าคือ“ อย่าบล็อกรหัส async” - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal

4
ฉันไม่เข้าใจ Task.Wait และ Task.Result ถูกทำลายโดยการออกแบบ? ทำไมพวกเขาถึงไม่ล้าสมัย?
osexpert

69

https://github.com/aspnet/Security/issues/59

"หนึ่งคำพูดสุดท้าย: คุณควรหลีกเลี่ยงการใช้Task.ResultและTask.Waitให้มากที่สุดเท่าที่จะเป็นไปได้ในขณะที่พวกเขาห่อหุ้มข้อยกเว้นใน AggregateExceptionและแทนที่ข้อความด้วยข้อความทั่วไป (เกิดข้อผิดพลาดอย่างน้อยหนึ่งข้อ) ซึ่งทำให้การดีบักยากขึ้นแม้ว่าเวอร์ชั่นซิงโครนัส ไม่ควรใช้บ่อยนักคุณควรพิจารณาใช้Task.GetAwaiter().GetResult()แทน "


20
แหล่งอ้างอิงที่นี่คือบางคนอ้างถึงคนอื่นโดยไม่มีการอ้างอิง พิจารณาบริบท: ฉันสามารถเห็นผู้คนจำนวนมากตาบอดได้โดยใช้ GetAwaiter () GetResult () ทุกที่หลังจากอ่านข้อความนี้
Jack Ukleja

2
ดังนั้นเราไม่ควรใช้มัน?
tofutim

11
หากสองภารกิจจบลงด้วยข้อยกเว้นคุณจะสูญเสียภารกิจที่สองในสถานการณ์Task.WhenAll(task1, task2).GetAwaiter().GetResult();นี้
พระคุณเจ้า

นี่เป็นอีกตัวอย่าง: github.com/aspnet/AspNetCore/issues/13611
George Chakhidze

33

ความแตกต่างอีกอย่างคือเมื่อasyncฟังก์ชั่นคืนค่าTaskแทนที่จะเป็นแบบTask<T>นั้นคุณจะไม่สามารถใช้งานได้

GetFooAsync(...).Result;

แต่ทว่า

GetFooAsync(...).GetAwaiter().GetResult();

ยังคงใช้งานได้

ฉันรู้รหัสตัวอย่างในคำถามสำหรับกรณีTask<T>อย่างไรก็ตามคำถามถูกถามโดยทั่วไป


1
นี่ไม่เป็นความจริง. ตรวจสอบซอของฉันที่ใช้โครงสร้างนี้ตรง: dotnetfiddle.net/B4ewH8
wojciech_rak

3
@wojciech_rak ในรหัสของคุณคุณกำลังใช้Resultด้วยGetIntAsync()ซึ่งผลตอบแทนที่ไม่ได้เป็นเพียงTask<int> Taskฉันแนะนำให้คุณอ่านคำตอบของฉันอีกครั้ง
นูรี Tasdemir

1
คุณกำลังขวาที่แรกที่ผมเข้าใจคุณตอบว่าคุณไม่สามารถGetFooAsync(...).Result ภายในTaskฟังก์ชั่นที่ให้ผลตอบแทน ตอนนี้เหมาะสมแล้วเนื่องจากไม่มี void Properties ใน C # ( Task.Resultเป็นคุณสมบัติ) แต่คุณสามารถเรียกใช้วิธี void ได้
wojciech_rak

22

awaitดังกล่าวแล้วว่าคุณสามารถใช้ หากคุณจำเป็นต้องเรียกใช้รหัสพร้อมกันเหมือนที่คุณพูดถึง.GetAwaiter().GetResult(), .Resultหรือ.Wait()เป็นความเสี่ยงในการติดตายเป็นจำนวนมากได้กล่าวว่าในความคิดเห็น / คำตอบ เนื่องจากพวกเราส่วนใหญ่ชอบ oneliners คุณสามารถใช้สิ่งเหล่านี้เพื่อ.Net 4.5<

การรับค่าผ่านเมธอด async:

var result = Task.Run(() => asyncGetValue()).Result;

เรียกวิธี async แบบซิงโครนัส

Task.Run(() => asyncMethod()).Wait();

Task.Runไม่มีปัญหาการหยุดชะงักจะเกิดขึ้นเนื่องจากการใช้งานของ

ที่มา:

https://stackoverflow.com/a/32429753/3850405


1

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

สำหรับงานที่ไม่ใช่แบบดั้งเดิม GetResult () มีค่าส่งคืนเป็นโมฆะ ฟังก์ชั่นที่มีประโยชน์ของมันจะถูกสร้างขึ้นใหม่เพื่อยกเว้นข้อยกเว้นเท่านั้น

แหล่งที่มา: c # 7.0 โดยย่อ

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