初めまして、嬉しいお言葉ありがとうございます。それは本当に素晴らしい機能であり、私はそれに少しでも参加できたことをうれしく思います.
すべてのコードがゆっくりと非同期になっている場合、デフォルトですべてを非同期にしないのはなぜですか?
まあ、あなたは誇張しています。すべてのコードが非同期になっていません。2 つの「プレーンな」整数を足し合わせると、結果を待っているわけではありません。2 つの将来の整数を加算して 3 番目の将来の整数を取得すると、それは将来アクセスする整数であるためTask<int>
、もちろん結果を待つことになります。
すべてを async にしない主な理由は、async/await の目的が、多くの高レイテンシ オペレーションが存在する環境でコードを記述しやすくすることであるためです。操作の大部分は高レイテンシーではないため、そのレイテンシーを軽減するパフォーマンス ヒットを取得しても意味がありません。むしろ、重要ないくつかの操作は高レイテンシーであり、それらの操作がコード全体に非同期のゾンビの蔓延を引き起こしています。
パフォーマンスが唯一の問題である場合、確かにいくつかの巧妙な最適化により、必要のないオーバーヘッドが自動的に削除されます。
理論的には、理論と実践は似ています。実際には、決してそうではありません。
この種の変換とそれに続く最適化パスに対する 3 つのポイントを挙げさせてください。
最初のポイントは次のとおりです。C#/VB/F# の async は、本質的に継続渡しの制限された形式です。関数型言語コミュニティでは、継続渡しスタイルを多用するコードを最適化する方法を特定する方法を見つけるために、膨大な量の研究が行われてきました。コンパイラ チームは、"async" がデフォルトであり、非非同期メソッドを識別して非非同期化する必要がある世界で、非常によく似た問題を解決しなければならない可能性があります。C# チームは、未解決の研究問題に取り組むことにあまり関心がないため、これは大きな反論となります。
反対の 2 番目のポイントは、C# には、この種の最適化をより扱いやすくする「参照透過性」のレベルがないことです。「参照透過性」とは、式の値が評価時に依存しないというプロパティを意味します。のような式2 + 2
は参照透過的です。必要に応じてコンパイル時に評価を行うか、実行時まで延期して同じ答えを得ることができます。ただし、x と y は時間の経過とともに変化する可能性があるx+y
ため、次のような式を時間内に移動することはできません。
非同期では、副作用がいつ発生するかを判断するのがはるかに難しくなります。非同期の前に、あなたが言った場合:
M();
N();
and M()
was void M() { Q(); R(); }
、 and N()
was void N() { S(); T(); }
、 and が副作用R
をS
生成する場合、R の副作用が S の副作用の前に発生することがわかります。しかし、もしそうならasync void M() { await Q(); R(); }
、突然それは窓の外に出ます。R()
が前後に発生するかどうかは保証されませんS()
(もちろんM()
が待機されている場合を除きます。ただし、もちろん、 が発生Task
するまで待機する必要はありませんN()
。)
副作用が発生する順序がわからないというこの特性が、プログラム内のすべてのコードに適用されることを想像してみてください。基本的に、どの式がどの順序で評価されるかはもうわかりません。つまり、すべての式が参照透過的である必要があり、これは C# のような言語では困難です。
反対の 3 つ目のポイントは、「なぜ async がそれほど特別なのか?」と自問する必要があることです。すべての操作が実際には である必要があると主張する場合はTask<T>
、「そうではない理由」という質問に答えることができる必要がありLazy<T>
ます。または「なぜNullable<T>
ですか?」または「なぜIEnumerable<T>
ですか?」それは同じくらい簡単にできるからです。すべての操作が nullable に持ち上げられるべきではないのはなぜですか? または、すべての操作が遅延計算され、後で使用できるように結果がキャッシュされます。または、すべての操作の結果が、単一の値ではなく一連の値になります。次に、「ああ、これは null であってはならないので、より良いコードを生成できる」などの状況を最適化する必要があります。
Task<T>
要点は、これほど多くの作業を正当化するのが実際にそれほど特別であることは私には明らかではありません。
これらの種類のことに興味がある場合は、Haskell などの関数型言語を調査することをお勧めします。これは、はるかに強力な参照透過性を持ち、あらゆる種類の順不同の評価を許可し、自動キャッシュを実行します。また、Haskell の型システムは、私が言及したような「モナディック リフティング」の種類をはるかに強力にサポートしています。