ここでは多少不満を表していますが、多くの新しいライブラリに非同期APIしかないのはなぜですか?たとえば、ウェブページを取得してそこからデータを解析する小さなユーティリティを作成しています。ただし、.netコアのSystem.Net.Http名前空間のHttpClientを使用するには、次の Webページ から、多くの非同期および待機ボイラープレートコードが必要です。
_async static Task Main(string[] args)
{
}
_
次に、Mainの内容:
_HttpClient client = new HttpClient();
var response = await client.GetAsync("http://www.nzherald.co.nz/");
var pageContents = await response.Content.ReadAsStringAsync();
Console.WriteLine(pageContents);
Console.ReadLine();
_
私はc#が非常に冗長な言語になっていると感じており、このような非同期スタイルでコーディングしなければならないのは幸せではありません。私は.net 1.0以来c#プログラムを書いてきましたが、コンパイラーの舞台裏で何が起こっているのかを推測するのは難しいと思います。非同期コードのスレッドを作成するとパフォーマンスに影響しますか?ループでGetAsyncを呼び出している場合、どのようにして呼び出しの速度を調整できますか?
私の質問は次のとおりです。C#チームは、言語でこの非同期パラダイムを作成する正しい方向に進んでいますか?私はgo myfunction()
と言うだけでスレッドを作成するアプローチのほうが時間がかかると感じていますが、グリーンスレッドが使用されているため、パフォーマンスが低下することはわかっています。
この質問 を見てください。たとえば、非同期に関する同じタイプの懸念があり、単一の回答はあまり役に立ちません。
私はc#が非常に冗長な言語になっていると感じており、このような非同期スタイルでコーディングしなければならないのは幸せではありません。
ああ、でもそれはまったく言い分ではありません。あなたはこのようなものを書いていません:
_client.GetAsync("http://www.nzherald.co.nz/").Then
(
response => response.Content.ReadAsStringAsync().Then
(
pageContents =>
{
Console.WriteLine(pageContents);
Console.ReadLine();
}
)
);
_
そして上記のようなAPI(おそらくContinueWith
のラッパー)は例外を適切に処理せず、例外を取得するためにコールバックを備えたError
メソッドがおそらく必要です。
いいえ、C#は冗長ではありません。
補遺:async
メソッドがTask
を返す場合でも、await
を実行する必要がないことを言及する価値があるかもしれません。 Task
で何か他のことをしたい場合(たとえば、_Task.WaitAll
_または_Task.WhenAll
_の配列を配置するなど)、await
キーワードは指定しません。したがって、await
キーワードは冗長ではありません。
非同期コードのスレッドの作成はパフォーマンスに影響しますか?
いいえ。一部のAPIは実際にはThread
でブロックする必要がありますが、少なくともWindowsではソケットをリッスンする場合はそうだと思いますが、これはThreadPool
を使用するため、新しいThread
sは多く作成されません。 。
ThreadPool
を使用することは、すべてのAPIに当てはまるわけではありません。ほとんどは次のパターンに従います。
ライブラリはTaskCompletionSource
を作成します。
ライブラリは、通知を受け取る手段を設定します。コールバック、タイマー、メッセージなど...
ライブラリは、受け取った通知に応じて、SetResult
またはSetException
でTaskCompletionSource
を呼び出す通知に反応するコードを設定します。
ライブラリは、外部システムを実際に呼び出します。
ライブラリは_TaskCompletionSource.Task
_を返します。
手順4からの呼び出しは、外部システムに送信されます。外部システムが応答し、ステップ2の通知メカニズムで応答が受信され、Task
が完了したことが設定されます。その後、Task
の継続がスケジュールされます(その継続は、await
の後に発生します)。
ループでGetAsyncを呼び出している場合、どのようにして呼び出しの速度を調整できますか?
それを待っている場合、コードは完了するまで続行されません。あるいは、限られた数のTask
を同時に(待たずに)持つことについて話している場合は、SemaphoreSlim
を使用することをお勧めします。 _Task.WaitAny
_または_Task.WhenAny
_と組み合わせて使用します。
それ以外にも、おそらくawait Task.Delay(milliseconds)
が役立つでしょう。タイマーの周りで、上記で説明したパターンを使用します(_Thread.Sleep
_とは異なり)Thread
は待機をブロックされません。
言語でこの非同期パラダイムを作成しているc#チームは正しい方向に進んでいますか?
私は_async/await
_が一般的に良いと信じています。オーバーヘッドがあることに同意します(Task
オブジェクトの作成など)。ただし、C#はほとんどの言語よりも優れています。
blue-red 引数を理解しています...
...その上で、純粋なコアと不純なシェルのアイデアを追いたい場合、純粋で不純なものとasync
と "sync"が似ていることがわかるでしょう。不純なシェルは外部システムを扱います。メソッドから不純なメソッドを呼び出す場合、メソッドは不純です。同様に、外部システムとのやり取りはしばしばasync
であり、await
をasync
メソッドにしたい場合は、メソッドをasync
にする必要があります。
つまり、そうです、async
の伝達は厄介です。解決策は、エントリポイントが不純(およびasync
)であり、外部システムを処理し(不純な命令型シェル)、not async
コード(純粋な機能コア)を呼び出し、シェルに戻って相互運用性を高めることです。そうすれば、すべてをasync
にする必要がなくなり、コード全体を不純にする必要もなくなります。外部システムを分離する場合(それらを交換しやすくし、テストを容易にするため)、おそらくすでにこれを行っています。
Go myfunction()と言ってスレッドを作成するgoのアプローチはそれほど時間のかかるものではないように感じますが、使用されている緑のスレッドによりパフォーマンスが低下することはわかっています。
await
はgo
よりも入力するのに時間がかかると不満がありますか?それは「冗長」を説明しています。
非同期はマルチスレッドを意味しません。たとえば、Thread
を待たずにハードディスクを読み取ることができます。読み取るバッファーを初期化し、オペレーティングシステムに、ハードディスクに(DMAを介して)バッファーに書き込むように指示し、イベントをトリガーするようにドライバーに指示するよう指示します。次に、イベントで_TaskCompletionSource.SetResult
_を実行すると、システムは継続をスケジュールできます(await
の後のコード)。 Thread
はありません。心配する必要はありませんでした。 そして、はい、それは私が上記で説明したのと同じパターンです。
コンパイラの舞台裏で何が起こっているのかを推測するのは難しいと思います
コンパイラは継続としてコードを書き換えています。ステートマシンです。コンパイラがコードをステートマシンとして書き換えるのはこれが初めてではありません。 コルーチン _yield return
_のイテレータ。
async
メソッドの場合、各await
は、コードがその後に続くTask
が続くことを意味します。
Thread
の呼び出しは待機しないことに注意してください。代わりに、asyncメソッドは最初のTask
をスケジュールし、その継続を設定し、メソッドに対して作成されたすべての継続が完了すると完了するTask
を返します。
実際には、エラー処理があり、_try-catch-finally
_で動作するため、少し複雑です。コンパイラが書き換えを行います。
これはおそらく、スケジュールされたThread
sを実行するために使用できる場合は、呼び出し元のTask
が最終的に自由に作業できることを意味します。継続は、それを呼び出したのと同じThread
で実行される場合もあれば、別のもので実行される場合もあります。 TaskScheduler
までです。デフォルトのTaskScheduler
はThreadPool
を使用します。 そして、はい、カスタムTaskScheduler
を作成できます。
おそらくインタビュー Mads Torgersen:Inside C#Async はさらに理解を深めるのに役立ちます。強く推奨する。
Webサーバーとその他のI/Oに高度にバインドされたアプリケーションのみを扱う場合は、非同期のものが多く、なくてはならないことを確認してください。ただし、IO非同期がそれほど有益ではなく、頻繁に使用されない場所にバインドされていないデスクトップおよびモバイルデバイス用のソフトウェアを作成している人はまだたくさんいます。
いずれにせよ、async/awaitは多くの人に共通の問題を解決する便利なツールであるため、多くのライブラリで使用されています。誰かがLINQやGenericsを使い始めたとき、またはC++ではなくC#を使い始めたときに、誰かが同じ質問をしたかもしれません。これは基本的に、アセンブリですべてを記述しないのと同じ理由です。
Webサーバーへの着信が100件あるとします。 100個すべてがSQLクエリの実行を開始します。呼び出しの99%は、SQLが結果を返すのを待つのに費やされています。
SQLが動作している間にスレッドをプールに戻し、別の要求を処理する方が良いでしょうか?
タスクを使用することで、UIコードを活用することもできます。 Unity 3DやCaliburn Microなどのタスクツールの前は、コルーチンを使用して非同期コードを同期的に実行していました。メインスレッド以外のスレッドではUIコンポーネントを操作できないため、これが必要です。また、非同期に実行しないと、UIが応答を停止します。 winformsでは、これを自分でマーシャリングする必要がありました。