Có phải Task.Result giống như .GetAwaiter.GetResult () không?


326

Gần đây tôi đã đọc một số mã sử dụng nhiều phương thức async, nhưng đôi khi cần phải thực thi chúng một cách đồng bộ. Mã này:

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

Cái này có giống như

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

8
Từ các tài liệu của GetResult: "Loại này và các thành viên của nó được dự định sử dụng bởi trình biên dịch." Người khác không nên sử dụng nó.
tiêu

32
Điều này được gọi là "đồng bộ hóa không đồng bộ" và trừ khi bạn biết cách thực hiện tác vụ có thể là một ý tưởng thực sự tồi tệ. Nó có thể bế tắc ngay lập tức trong nhiều trường hợp ( ví dụ async/ một awaitphương thức trong MVC)
Marc Gravell


13
Trong thế giới thực, chúng tôi có các nhà xây dựng, chúng tôi có các giao diện "không chờ đợi" mà chúng tôi cần triển khai và chúng tôi được cung cấp các phương thức không đồng bộ ở mọi nơi. Tôi sẽ hài lòng khi sử dụng thứ gì đó chỉ hoạt động mà không phải tự hỏi tại sao nó "nguy hiểm", "không được sử dụng" hay "tránh bằng mọi giá". Mỗi lần tôi phải loay hoay với async thì lại đau đầu.
Larry

Câu trả lời:


173

Khá nhiều. Một sự khác biệt nhỏ: nếu Taskthất bại, GetResult()sẽ chỉ ném ngoại lệ gây ra trực tiếp, trong khi Task.Resultsẽ ném một AggregateException. Tuy nhiên, quan điểm của việc sử dụng một trong những điều đó là gìasync gì? Tùy chọn tốt hơn 100 lần là sử dụng await.

Ngoài ra, bạn không có ý định sử dụng GetResult(). Nó chỉ dành cho trình biên dịch, không dành cho bạn. Nhưng nếu bạn không muốn gây phiền nhiễu AggregateException, hãy sử dụng nó.


27
@JayBazuzi Không phải nếu khung kiểm tra đơn vị của bạn hỗ trợ kiểm tra đơn vị không đồng bộ, mà tôi nghĩ các phiên bản mới nhất của hầu hết các khung làm.
Svick

15
@JayBazuzi: MSTest, xUnit và NUnit tất cả các bài async Taskkiểm tra đơn vị hỗ trợ và đã có một thời gian.
Stephen Cleary

18
đẩy lùi 100x - tệ hơn 1000 lần khi sử dụng chờ đợi nếu bạn thích ứng mã cũ và sử dụng await yêu cầu viết lại.
bị kẹt vào

13
@AlexZhukovskiy: Tôi không đồng ý .
Stephen Cleary

15
The 100x better option is to use await.Tôi ghét những tuyên bố như thế này, nếu tôi có thể tát awaittrước mặt tôi sẽ làm. Tuy nhiên, khi tôi đang cố gắng để lấy mã async để làm việc chống lại các mã phi async giống như những gì thường xuyên xảy ra với tôi rất nhiều trong Xamarin, tôi sẽ chỉ phải sử dụng những thứ như ContinueWithrất nhiều để làm cho nó không bế tắc UI. Chỉnh sửa: Tôi biết điều này đã cũ, nhưng điều đó không làm giảm bớt sự thất vọng của tôi khi tìm câu trả lời cho biết điều này không có lựa chọn thay thế cho các tình huống mà bạn không thể sử dụng await.
Thomas F.

144

Task.GetAwaiter().GetResult()được ưa thích hơn Task.WaitTask.Resultbởi vì nó truyền bá các ngoại lệ thay vì gói chúng trong một AggregateException. Tuy nhiên, cả ba phương pháp này đều gây ra khả năng gây ra sự cố chết đói và luồng xử lý. Tất cả chúng nên được tránh để ủng hộasync/await .

Câu trích dẫn dưới đây giải thích tại sao Task.WaitTask.Resultkhông chỉ đơn giản chứa hành vi lan truyền ngoại lệ của Task.GetAwaiter().GetResult()(do "thanh tương thích rất cao").

Như tôi đã đề cập trước đây, chúng tôi có một thanh tương thích rất cao và do đó chúng tôi đã tránh phá vỡ các thay đổi. Như vậy,Task.Wait giữ lại hành vi ban đầu của nó luôn luôn gói. Tuy nhiên, bạn có thể thấy mình trong một số tình huống nâng cao khi bạn muốn hành vi tương tự như chặn đồng bộ được sử dụng bởi Task.Wait, nhưng ở đó bạn muốn ngoại lệ ban đầu được truyền bá không được bao bọc thay vì được đặt trong một AggregateException. Để đạt được điều đó, bạn có thể nhắm mục tiêu trực tiếp đến người phục vụ của Nhiệm vụ. Khi bạn viết NỀN await task;, trình biên dịch sẽ dịch nó thành cách sử dụng Task.GetAwaiter()phương thức, nó trả về một thể hiện có GetResult()phương thức. Khi được sử dụng trên một Nhiệm vụ bị lỗi, GetResult()sẽ truyền bá ngoại lệ ban đầu (đây là cách mà Nghi phạmawait task; Martin Tấn có hành vi của nó). Do đó, bạn có thể sử dụngtask.GetAwaiter().GetResult()Nếu bạn muốn trực tiếp gọi logic truyền bá này.

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

GetResult” Thực sự có nghĩa là “kiểm tra các nhiệm vụ cho các lỗi”

Nói chung, tôi cố gắng hết sức để tránh chặn đồng bộ trên một tác vụ không đồng bộ. Tuy nhiên, có một số tình huống tôi vi phạm nguyên tắc đó. Trong những điều kiện hiếm hoi đó, phương pháp ưa thích của tôi là GetAwaiter().GetResult()vì nó bảo tồn các ngoại lệ của nhiệm vụ thay vì gói chúng trong mộtAggregateException .

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


3
Vì vậy, về cơ bản Task.GetAwaiter().GetResult()là tương đương với await task. Tôi giả sử tùy chọn đầu tiên được sử dụng khi phương thức không thể được đánh dấu bằng async(ví dụ hàm tạo). Đúng không? Nếu có, thì nó va chạm với câu trả lời hàng đầu @ ItNotALie
OlegI

5
@OlegI: Task.GetAwaiter().GetResult()tương đương hơn Task.WaitTask.Result(trong đó cả ba sẽ chặn đồng bộ và có khả năng gây bế tắc), nhưng Task.GetAwaiter().GetResult()có hành vi lan truyền ngoại lệ của nhiệm vụ đang chờ.
Nitin Agarwal

Bạn không thể tránh các bế tắc trong kịch bản này với (Nhiệm vụ) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz

3
@DanielLorenz: Xem trích dẫn sau: "Sử dụng ConfigureAwait (false) để tránh bế tắc là một hành vi nguy hiểm. Bạn sẽ phải sử dụng ConfigureAwait (false) cho mọi chờ đợi trong việc đóng tạm thời tất cả các phương thức được gọi bởi mã chặn, kể cả thứ ba - và mã của bên thứ hai. Sử dụng ConfigureAwait (false) để tránh bế tắc tốt nhất chỉ là một bản hack). ... giải pháp tốt hơn là chặn Đừng chặn trên mã không đồng bộ mã hóa. " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal

4
Tôi không hiểu Task.Wait và Task.Result bị phá vỡ bởi thiết kế? Tại sao họ không làm cho lỗi thời?
osexpert

69

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

"Một lưu ý cuối cùng: bạn nên tránh sử dụng Task.ResultTask.Waitcàng nhiều càng tốt vì chúng luôn gói gọn ngoại lệ bên trong trong AggregateExceptionvà thay thế tin nhắn bằng một lỗi chung (Một hoặc nhiều lỗi xảy ra), khiến việc gỡ lỗi khó hơn. Ngay cả khi phiên bản đồng bộ không nên Không được sử dụng thường xuyên, bạn nên cân nhắc sử dụng Task.GetAwaiter().GetResult()thay thế. "


20
Nguồn được tham chiếu ở đây là một người trích dẫn người khác, không có tài liệu tham khảo. Xem xét bối cảnh: Tôi có thể thấy rất nhiều người mù quáng sử dụng GetAwaiter (). GetResult () ở mọi nơi sau khi đọc nó.
Jack Ukleja

2
Vì vậy, chúng ta không nên sử dụng nó?
tofutim

11
Nếu hai nhiệm vụ kết thúc bằng một ngoại lệ, bạn sẽ mất cái thứ hai trong kịch bản này Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Đức ông


33

Một sự khác biệt nữa là khi asynchàm trả về Taskthay vì Task<T>sau đó bạn không thể sử dụng

GetFooAsync(...).Result;

Trong khi

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

vẫn hoạt động.

Tôi biết mã ví dụ trong câu hỏi là dành cho trường hợp Task<T>, tuy nhiên câu hỏi thường được hỏi.


1
Đây không phải là sự thật. Kiểm tra fiddle của tôi sử dụng chính xác cấu trúc này: dotnetfiddle.net/B4ewH8
wojciech_rak

3
@wojciech_rak Trong code của bạn, bạn đang sử dụng Resultvới GetIntAsync()đó lợi nhuận Task<int>không chỉ Task. Tôi đề nghị bạn đọc câu trả lời của tôi một lần nữa.
Nuri Tasdemir

1
Bạn nói đúng, lúc đầu tôi hiểu bạn trả lời rằng bạn không thể GetFooAsync(...).Result ở trong hàm trả về Task. Điều này bây giờ có ý nghĩa, vì không có thuộc tính void trong C # ( Task.Resultlà một thuộc tính), nhưng tất nhiên bạn có thể gọi một phương thức void.
wojciech_rak

22

Như đã đề cập nếu bạn có thể sử dụng await. Nếu bạn cần chạy mã đồng bộ như bạn đề cập .GetAwaiter().GetResult(), .Resulthoặc.Wait() có nguy cơ bị bế tắc như nhiều người đã nói trong các bình luận / câu trả lời. Vì hầu hết chúng ta thích oneliners, bạn có thể sử dụng chúng cho.Net 4.5<

Có được giá trị thông qua phương thức async:

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

Gọi đồng bộ một phương thức async

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

Không có vấn đề bế tắc sẽ xảy ra do việc sử dụng Task.Run.

Nguồn:

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


1

Nếu một tác vụ lỗi, ngoại lệ được ném lại khi mã tiếp tục gọi Waititer.GetResult (). Thay vì gọi GetResult, chúng ta chỉ cần truy cập vào thuộc tính Kết quả của tác vụ. Lợi ích của việc gọi GetResult là nếu tác vụ bị lỗi, ngoại lệ được ném trực tiếp mà không được bọc trong AggregateException, cho phép các khối bắt đơn giản và gọn gàng hơn.

Đối với các tác vụ không phổ biến, GetResult () có giá trị trả về void. Chức năng hữu ích của nó sau đó chỉ là để lấy lại ngoại lệ.

nguồn: c # 7.0 trong một Nutshell

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.