web-dev-qa-db-ja.com

C#の「return await」の目的は何ですか?

anyこのようなメソッドを書くシナリオがあります:

public async Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return await DoAnotherThingAsync();
}

これの代わりに:

public Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return DoAnotherThingAsync();
}

理にかなっていますか?

内部DoAnotherThingAsync()呼び出しからreturn awaitを直接返すことができる場合にTask<T>コンストラクトを使用する理由?

return awaitを含むコードは非常に多くの場所で見られますが、何かを見逃している可能性があります。しかし、私が理解している限りでは、この場合async/awaitキーワードを使用せず、タスクを直接返すことは機能的に同等です。追加のawaitレイヤーのオーバーヘッドを追加する理由

211
TX_

asyncが不要な場合(つまり、Taskを直接返すことができる場合)、asyncを使用しないでください。

two非同期操作を行う場合など、return awaitが役立つ状況がいくつかあります。

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

asyncパフォーマンスの詳細については、トピックに関するStephen Toubの MSDN記事 および video を参照してください。

更新:もっと詳しく説明する ブログ投稿 を書きました。

81
Stephen Cleary

これを行う唯一の理由は、以前のコードに他のawaitがある場合、または何らかの方法で結果を操作してから結果を返す場合です。それが発生する可能性のある別の方法は、例外の処理方法を変更するtry/catchを使用することです。あなたがそれを何もしていないなら、あなたは正しい、メソッドasyncを作るオーバーヘッドを追加する理由はありません。

23
Servy

結果を待つ必要がある別のケースは次のとおりです。

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

この場合、GetFooAsyncのタイプは2つのメソッド間で異なり、Task<Foo>Task<IFoo>に直接割り当てることができないため、GetIFooAsync()Tの結果を待つ必要があります。 。しかし、結果を待つ場合、それはFooになります。これはisが直接IFooに割り当て可能です。次に、asyncメソッドは、結果をTask<IFoo>内に再パッケージし、すぐに離れます。

14
Andrew Arnott

それ以外の点で単純な「サンク」メソッドを非同期にすると、非同期ステートマシンがメモリに作成されますが、非同期ではありません。非同期バージョンはより効率的であるため(実際には)非非同期バージョンを使用することを指すことがよくありますが、ハングした場合には、そのメソッドが「戻り/継続スタック」に関与しているという証拠もありません。ハングを理解するのが難しくなる場合があります。

そのため、perfが重要でない(通常はそうではない)場合は、これらのすべてのサンクメソッドで非同期をスローして、後でハングを診断し、それらがサンクメソッドは時間の経過とともに進化するため、スローではなくフォールトされたタスクを返すようになります。

4
Andrew Arnott

Return awaitを使用しない場合は、デバッグ中または例外のログに出力されるときにスタックトレースを台無しにする可能性があります。

タスクを返すと、メソッドは目的を果たし、コールスタックから除外されます。 return awaitを使用すると、呼び出しスタックに残ります。

例えば:

Awaitを使用する場合の呼び出しスタック:AはBからのタスクを待機== BはCからのタスクを待機

notを使用してスタックを呼び出すawaitを使用:AがCからタスクを待機し、Bが返しました。

4
haimb

これも私を混乱させ、以前の答えはあなたの実際の質問を見過ごしていたと感じています:

内側のDoAnotherThingAsync()呼び出しから直接Taskを返すことができるのに、return awaitコンストラクトを使用するのはなぜですか?

まあ時々実際Task<SomeType>が欲しいのですが、ほとんどの場合、実際にはSomeTypeのインスタンス、つまりタスクの結果が必要です。

コードから:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

構文に慣れていない人(たとえば、私)は、このメソッドがTask<SomeResult>を返すべきだと考えるかもしれませんが、asyncでマークされているため、実際の戻り型はSomeResultであることを意味します。 return foo.DoAnotherThingAsync()を使用するだけの場合、コンパイルされないTaskを返します。正しい方法は、タスクの結果を返すことです。したがって、return awaitです。

2
heltonbiker