Task.Resultは.GetAwaiter.GetResult()と同じですか?


328

最近、多くの非同期メソッドを使用するコードを読んでいましたが、それらを同期的に実行する必要がある場合があります。コードは:

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

これは同じですか

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

8
のドキュメントからGetResult:「この型とそのメンバーはコンパイラによる使用を目的としています。」他の人はそれを使うべきではありません。
消費者2013年

32
これは「sync over async」と呼ばれ、タスクがどのように実装されているかを知らない限り非常に悪い考えです。多くの場合、即座にデッドロックする可能性があります(たとえば、MVCのasync/ awaitメソッド)
Marc Gravell


14
実際には、コンストラクターがあり、実装する必要のある「待機なし」のインターフェースがあり、あらゆる場所に非同期メソッドが与えられています。なぜ「危険」、「使わない」、「絶対に避ける」のか迷うことなく機能するものを使ってみたいです。非同期をいじる必要があるたびに、頭痛の種になります。
ラリー

回答:


173

かなり。ただし、小さな違いが1つあります。Task失敗した場合、GetResult()直接発生した例外がスローされるだけで、Task.ResultはスローされますAggregateException。ただし、どちらを使用するのasyncですか?100倍良いオプションはを使用することawaitです。

また、を使用するためのものではありませんGetResult()。それはあなたのためではなく、コンパイラの使用のみを目的としています。しかし、迷惑なものAggregateExceptionにしたくない場合は、それを使用してください。


27
@JayBazuziユニットテストフレームワークが非同期ユニットテストをサポートしている場合は、ほとんどのフレームワークの最新バージョンでサポートされていると思います。
2013年

15
@JayBazuzi:MSTest、xUnit、およびNUnitはすべて単体テストをサポートasync Taskしており、しばらくは使用できます。
Stephen Cleary 2013年

18
100倍に押し戻す-古いコードを適合させてawaitを使用する場合は、awriteを使用すると書き換えが必要になるため、1000倍悪い。
スタック

13
@AlexZhukovskiy:同意しません
Stephen Cleary

15
The 100x better option is to use await.私はこのような発言が嫌いです、もし私がawaitそれの前で平手打ちできたら私はそうします。私は頻繁に私に何が起こるかのような非非同期コードに対する作業に非同期コードを取得しようとしているときには、多くのことを Xamarinには、私のようなものを使用する必要が終わるContinueWithそれがUIをデッドロックではないようにするために多くの。編集:私はこれが古いことを知っていますが、それだけでは使用できない状況に代わるものがなく、これを述べている答えを見つける私の欲求不満を軽減しませんawait
トーマス

147

Task.GetAwaiter().GetResult()好まれるTask.WaitTask.Result、それはそれらをラップするのではなく、例外を伝播しているためAggregateException。ただし、3つすべての方法では、デッドロックとスレッドプール不足の問題が発生する可能性があります。彼らはすべてを支持して避けるべきですasync/await

以下の引用は、理由Task.Waitを説明しておりTask.ResultTask.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.Waitandと同等ですがTask.Result(3つすべてが同期的にブロックされ、デッドロックの可能性がありTask.GetAwaiter().GetResult()ます)、awaitタスクの例外伝播動作があります。
Nitin Agarwal

(Task).ConfigureAwait(false).GetAwaiter()。GetResult();を使用して、このシナリオでデッドロックを回避することはできません。?
Daniel Lorenz

3
@DanielLorenz:次の引用を参照してください:「デッドロックを回避するためにConfigureAwait(false)を使用することは危険な慣行です。すべての3番目を含む、ブロッキングコードによって呼び出されるすべてのメソッドの推移的クロージャですべての待機に対してConfigureAwait(false)を使用する必要があります。 -とセカンドパーティのコード。ConfigureAwait(false)を使用してデッドロックを回避することは、せいぜいハックです... ...より良い解決策は、「非同期コードをブロックしない」です。- blog.stephencleary.com/2012/07/dont-block-on-async-code.html
ニティンAgarwalさん

4
わかりません。Task.WaitとTask.Resultは意図的に壊れていますか?彼らはなぜ時代遅れにならないのですか?
osexpert 2018

69

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

「最後の一つの発言:あなたが使用して避けなければならないTask.ResultTask.Wait、彼らは常にで内部例外をカプセル化するようできるだけ AggregateExceptionと難しく、デバッグになり、一般的な1(1つの以上のエラーが発生した)、でメッセージを交換する場合でも、同期バージョンshouldn。頻繁に使用しないでくださいTask.GetAwaiter().GetResult()。代わりに使用することを強く検討する必要があります。」


20
ここで参照されるソースは、参照なしで誰かが他の人を引用しているものです。コンテキストを検討してください。これを読んだ後、多くの人が盲目的にGetAwaiter()。GetResult()を使用しているのを見ることができます。
Jack Ukleja、2016年

2
だから私たちはそれを使うべきではないのですか?
tofutim

11
2つのタスクが例外で終了した場合、このシナリオでは2番目のタスクが失われますTask.WhenAll(task1, task2).GetAwaiter().GetResult();
Monsignor 2017


33

もう1つの違いは、async関数が次のTask代わりに戻るTask<T>場合です。

GetFooAsync(...).Result;

一方

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

まだ動作します。

質問のコード例はケース用であることはわかっていますが、質問はTask<T>一般的に行われます。


1
本当じゃない。まさにこの構成を使用する私のフィドルをチェックしてください:dotnetfiddle.net/B4ewH8
wojciech_rak

3
@wojciech_rakコードでは、Resultwith GetIntAsync()を使用してTask<int>、だけでなくも返しますTask。私の答えをもう一度読むことをお勧めします。
Nuri Tasdemir

1
あなたが正しい、最初に私はあなたが返す関数のGetFooAsync(...).Result 中に入れることはできないとあなたが答えると理解したTask。C#にTask.Resultはvoidプロパティがないため(これはプロパティです)、これは当然のことですが、もちろんvoidメソッドを呼び出すことができます。
wojciech_rak

22

あなたが使用できるかどうかすでに述べたようにawait。あなたが言及するようにコードを同期的に実行する必要がある場合.GetAwaiter().GetResult().Resultまたは.Wait()多くの人がコメント/回答で述べているようにデッドロックのリスクがある場合。私たちの多くはワンライナーが好きなので、これらを次の用途に使用できます.Net 4.5<

非同期メソッドを介して値を取得する:

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

非同期メソッドを同期的に呼び出す

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

の使用によるデッドロックの問題は発生しませんTask.Run

ソース:

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


1

タスクが失敗した場合、継続コードがawaiter.GetResult()を呼び出すと、例外が再スローされます。GetResultを呼び出すのではなく、タスクのResultプロパティにアクセスするだけで済みます。GetResultを呼び出すことの利点は、タスクが失敗した場合、例外がAggregateExceptionにラップされずに直接スローされるため、よりシンプルでクリーンなcatchブロックが可能になることです。

非ジェネリックタスクの場合、GetResult()にはvoidの戻り値があります。その場合、その便利な機能は、例外を再スローすることだけです。

ソース:簡単に言うとc#7.0

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.