web-dev-qa-db-ja.com

C#5.0の非同期関数と通常の関数の間の線をぼかす

最近、C#5.0の素晴らしい async-await パターンを十分に理解できていないようです。あなたは私の人生のどこにいましたか?

単純な構文には本当に感激していますが、小さな難しさがあります。私の問題は、非同期関数の宣言が通常の関数とはまったく異なることです。他の非同期関数で待機できるのは非同期関数だけなので、古いブロックコードを非同期に移植しようとすると、変換しなければならない関数のドミノ効果があります。

人々はこれをゾンビ感染と呼んでいます。 asyncがコードに噛み付くと、ますます大きくなります。移植プロセスは難しくありません。宣言でasyncをスローし、戻り値をTask<>でラップするだけです。しかし、古い同期コードを移植する場合、これを何度も繰り返すのは面倒です。

両方の関数タイプ(asyncとplain old sync)がまったく同じ構文を持っていると、もっと自然に思えます。これが事実であれば、移植にかかる労力はゼロになり、2つの形式を簡単に切り替えることができます。

私はこれらのルールに従えばこれはうまくいくと思います:

  1. 非同期関数はasync宣言を必要としなくなりました。それらの戻り値の型はTask<>でラップする必要はありません。コンパイラーは、コンパイル中にそれ自体で非同期関数を識別し、必要に応じてTask <>ラッピングを自動的に実行します。

  2. 非同期関数の呼び出しを忘れることはありません。非同期関数を呼び出したい場合は、それを待つ必要があります。私はとにかくファイアアンドフォーゲットをほとんど使用していません。クレイジーレースコンディションやデッドロックのすべての例は、常にそれらに基づいているようです。私はそれらが私たちが活用しようとしている同期の考え方とあまりに混乱していて「連絡がない」と思います。

  3. 本当に忘れずに生きられないのなら、それのための特別な構文があります。いずれにしても、それは私が話している単純な統一された構文の一部ではありません。

  4. 非同期呼び出しを示す必要がある唯一のキーワードはawaitです。待機している場合、呼び出しは非同期です。そうでない場合、呼び出しは従来の同期です(覚えておく必要はありません)。

  5. コンパイラーは非同期関数を自動的に識別します(関数には特別な宣言がないため)。ルール4を使用すると、これを非常に簡単に実行できます。関数の内部にawait呼び出しがある場合は、非同期です。

これでうまくいきますか?または私は何かを逃していますか?この統一された構文ははるかに流動的で、ゾンビの蔓延を完全に解決できます。

いくつかの例:

// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }

// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }

// now let's try all combinations and see what they do:

// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();

// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();

// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();

// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();

注:これは、SOの興味深い 関連するディスカッション の続きです。

10
talkol

あなたの質問は、リンクしたSO質問ですでに回答されています。

Async/awaitの目的は、レイテンシの高い操作が多い環境でコードを簡単に記述できるようにすることです。 操作の大部分は高遅延ではありません

WinRT が最初に出たとき、設計者は、どの操作を非同期にするかをどのように決定したかを説明していました。彼らは、50ms以上かかるものはすべて非同期であり、残りのメソッドは通常の非同期メソッドであることを決定しました。

非同期にするには、いくつのメソッドを書き直す必要がありましたか?それらの約10パーセント。他の90%はまったく影響を受けませんでした。

エリックリッペルトは、ワンサイズのアプローチを採用しないことを選択した理由をかなりかなりの技術的詳細で説明し続けます。彼は基本的に、asyncawaitは継続渡しスタイルの部分的な実装であり、すべてのケースに適合するようにそのようなスタイルを最適化することは難しい問題であると述べています。

9
Robert Harvey