web-dev-qa-db-ja.com

なぜメソッドを「待って」、すぐに戻り値を問い合わせるのでしょうか?

このMSDN記事 では、次のコード例が提供されています(簡潔にするために少し編集しています)。

_public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    Department department = await db.Departments.FindAsync(id);

    if (department == null)
    {
        return HttpNotFound();
    }

    return View(department);
}
_

FindAsyncメソッドは、IDによってDepartmentオブジェクトを取得し、_Task<Department>_を返します。次に、部門がすぐにチェックされ、それがnullかどうかが確認されます。私が理解しているように、この方法でタスクの値を要求すると、待機中のメソッドから値が返されるまでコードの実行がブロックされ、これが事実上同期になりますコール。

なぜこれをやるのですか?とにかくすぐにブロックするつもりなら、同期メソッドFind(id)を呼び出す方が簡単ではないでしょうか?

28
Robert Harvey

私が理解しているように、この方法でタスクの値を要求すると、待機中のメソッドから値が返されるまでコードの実行がブロックされ、事実上これが同期呼び出しになります。

結構です。

await db.Departments.FindAsync(id)を呼び出すと、タスクが送信され、現在のスレッドが他の操作で使用できるようにプールに返されます。実行のフローはブロックされますが(正しく理解できれば、直後にdepartmentを使用する場合と同じように)、操作が完了するまでスレッド自体が自由に使用できます。オフマシンで完了しました(イベントまたは完了ポートによって通知されます)。

d.Departments.Find(id)を呼び出した場合、ほとんどの処理がDBで行われている場合でも、スレッドはそこに座って応答を待ちます。

ディスクがバインドされると、CPUリソースを効果的に解放します。

26
Telastyn

タスクを待つ前に数行待つことがどのように可能であるかを示す例はどれもないのが本当に嫌いです。このことを考慮。

Foo foo = await getFoo();
Bar bar = await getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(foo, bar);

これはサンプルが推奨する種類のコードであり、あなたの言うとおりです。これにはほとんど意味がありません。メインスレッドを解放して、UI入力への応答など、他のことを行うことができますが、非同期/待機の本当の力は、長時間実行される可能性のあるタスクが完了するのを待っている間、他のことを簡単に続けることができることです。上記のコードは「ブロック」し、Foo&Barが取得されるまで印刷行の実行を待機します。でも待つ必要はありません。待っている間に処理できます。

Task<Foo> foo = getFoo();
Task<Bar> bar = getBar();

Console.WriteLine(“Do some other stuff to prepare.”);

doStuff(await foo, await bar);

これで、書き換えられたコードにより、停止する必要がなくなり、値を待つ必要がなくなります。私はいつもこのような機会に目を光らせています。私たちが待っているときに頭が良いと、パフォーマンスが大幅に向上する可能性があります。最近は複数のコアがあり、それらを使用することもできます。

21
RubberDuck

したがって、ここでは裏で行われることがさらにあります。 Async/Awaitは構文糖衣です。まず、FindAsync関数のシグネチャを確認します。タスクを返します。すでにキーワードの魔法を見ているので、そのタスクを部門に箱から出します。

呼び出し元の関数はブロックしません。何が起こるかというと、部門への割り当てとawaitキーワードに続くすべてがクロージャーにボックス化され、すべての意図と目的のために Task.ContinueWithメソッド に渡されます(FindAsync関数は別のスレッドで自動的に実行されます) )。

もちろん、操作が元のスレッドにマーシャリングされるため(バックグラウンド操作を実行するときにUIと同期することを心配する必要がなくなるため)、呼び出し側の関数が非同期(非同期で呼び出される)同じことがスタックで起こります。

したがって、何が起こるかというと、落とし穴なしで非同期操作の魔法が得られるということです。

6
Michael Brown

いいえ、すぐには戻りません。 awaitはメソッド呼び出しを非同期にします。 FindAsyncが呼び出されると、Detailsメソッドは終了していないタスクを返します。 FindAsyncが完了すると、その結果が部門変数に返され、残りのDetailsメソッドが再開されます。

1
Steve

「非同期」はコントラクトのように考えるのが好きです。「必要に応じて非同期に実行できますが、他の同期関数のように呼び出すこともできます」というコントラクトです。

つまり、1人の開発者が関数を作成していて、いくつかの設計上の決定により、一連の関数を「非同期」として作成/マーク付けしました。関数の呼び出し元/消費者は、自由にそれらを使用できます。あなたが関数呼び出しの直前にawaitを呼び出してそれを待つことができると言うように、このように同期関数のように扱いましたが、必要に応じて、

Task<Department> deptTask = db.Departments.FindAsync(id);

そして、例えば、あなたが呼び出す関数の10行後

Department d = await deptTask;

したがって、非同期関数として扱います。

それはあなた次第です。

1
user734028