1. ホーム
  2. c#

[解決済み】C#でValueTask<T>ではなくTask<T>を使用するのはなぜですか?

2022-04-05 10:48:29

疑問点

C#7.0から、非同期メソッドはValueTask<T>を返すことができるようになりました。説明では、キャッシュされた結果があるときや、同期的なコードで非同期をシミュレートするときに使用するようにとあります。しかし、ValueTaskを常に使うことの何が問題なのか、実際、なぜasync/awaitが最初からvalue型で作られていないのか、私にはまだ理解できていません。ValueTaskが仕事をしないのはどんな場合でしょうか?

解決方法は?

から API ドキュメント (強調)。

<ブロッククオート

メソッドは、操作の結果が同期的に利用できる可能性が高い場合、この値型のインスタンスを返すことがあります。 そして メソッドが頻繁に呼び出されることが予想され、そのために新しい Task<TResult> を呼び出すことは、法外な負担となる。

を使用することにはトレードオフがあります。 ValueTask<TResult> の代わりに Task<TResult> . 例えば ValueTask<TResult> は、成功した結果が同期的に利用可能である場合に、割り当てを回避するのに役立ちますが、2つのフィールドを含んでいます。 Task<TResult> を参照型とした場合、1つのフィールドとなります。これは、メソッド呼び出しが1つではなく2つのフィールドに相当するデータを返すことになり、コピーするデータが多くなることを意味します。また、もしこのうちのひとつを返すメソッドが async メソッドのステートマシンは、その async メソッドは、1 つの参照ではなく 2 つのフィールドである構造体を格納する必要があるため、サイズが大きくなります。

さらに、非同期処理の結果を消費する以外の用途で await , ValueTask<TResult> は、より複雑なプログラミングモデルにつながる可能性があり、その結果、実際にはより多くのアロケーションにつながる可能性があります。たとえば、あるメソッドが Task<TResult> キャッシュされたタスクを共通の結果として持つか、あるいは ValueTask<TResult> . 結果の消費者が、その結果を Task<TResult> のようなメソッドで使用するような場合です。 Task.WhenAllTask.WhenAny を使用します。 ValueTask<TResult> に変換する必要があります。 Task<TResult> を使って AsTask これは、キャッシュされた Task<TResult> が使用されていた。

このように 非同期メソッドでは、デフォルトで Task または Task<TResult> . パフォーマンス分析によって価値があることが証明された場合にのみ ValueTask<TResult> の代わりに使用されます。 Task<TResult> .