答えは複雑です。現在のコンパイラの await の実装は、いくつかの点でコールバックよりも優れていますが、場合によっては劣っています。
.NET 実行コンテキスト: await と ContinueWith(...) の両方で .NET 実行コンテキストをキャプチャして復元する予定です。そうしないと、資格情報などの任意のものを取得して、次の作業項目のためにスレッドプールに残すことができるため、.NET の安全要件に合格しません。「await」については、これは内部ビルドで行った調整ですが、//BUILD デベロッパー プレビューを作成した後でした。
メモリ割り当て: いくつかの点で、'await' は手動コールバックよりもメモリ割り当てに適しています。重要なのは、多くの await を持つ関数の場合、実際に生成しているのは複数のコールバックに相当するということです。直線的な実行順序で 5 つの await があり、実行が常に最後まで流れている場合、同等の処理には 5 つのコールバックが必要です。これら 5 つのコールバックのそれぞれについて、個別のラムダ クロージャ オブジェクトと、その特定のラムダを表すデリゲートを生成できます。「待機」の場合、コンパイラは、デリゲート オブジェクトを他の目的で使用しないことを認識しています。代わりに、メソッド全体で 1 つのクロージャーと 1 つのデリゲートを共有し、メソッド内のどこにいるかを追跡する内部ステート マシンを使用します。したがって、この場合、「await」はより少ないオブジェクトを割り当てます。
ショートカットの'Await' には、単なるコールバックよりも洗練されたセマンティクスもあります。コールバック ラムダを作成している場合、コンパイラはクロージャとラムダのエントリポイント デリゲートの割り当てを強制されます。'await' の場合、await コントラクトは、既に "完了" している awaitable のより最適化されたコードパスを可能にします。await が評価される前に awaitable が「完了」したと言う場合、セマンティックは結果をヤンクする純粋なパススルーです。これは、実際に必要になるまでコンパイラが割り当てを遅らせる可能性があることを意味します。したがって、実際に必要でない限り、クロージャ割り当て、デリゲート割り当て、スケジューリング コストを支払うことはありません。現在の Developer Preview コンパイラには、これらのパフォーマンスの最適化が含まれています。
パフォーマンスと引き換えに危険.NET セキュリティ モデルを本当にバイパスしたい場合は、実行コンテキストのパッケージ/復元を回避することでパフォーマンスを少し向上させることができるケースを想像することができます。コンテキストをキャプチャ/復元する必要があります。ただし、ほとんどの .NET のメソッドはそれを暗黙のうちに実行するため、どのメソッドがそれなしで生のアクセスを提供するかを本当に知る必要があります。.NET の経験則では、API が部分的な信頼 (Silverlight など) で利用できる場合、API は呼び出されたときに確実にコンテキストをキャプチャし、実行を別の場所に転送する API (ContinueWith、QueueUserWorkItem( など) の場合はそれを復元します。 ...) など)。デリゲートをキューに入れるだけの独自のスレッドプールをロールする場合、これをバイパスできますが、ほとんどの場合、必要ありません。
個人的なおすすめ使用待機。それはより高いレベルであり、それはあなたが望むものです。このリリースに合わせて調整するためにかなりの労力を費やしましたが、おそらくさらに調整することができます。コールバックベースの API は、言語規則に違反し始める前にコンパイラが調整できる範囲が限定されるため、より制限されます。メソッドで await を使用すると、コールバックよりもスマートなクロージャーを使用できます。そして... await は、コールバックよりもはるかに直感的に読み取ったり使用したりできます:)