web-dev-qa-db-ja.com

新しいAPIをすべて非同期にする必要があるのはなぜですか?

ここでは多少不満を表していますが、多くの新しいライブラリに非同期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()と言うだけでスレッドを作成するアプローチのほうが時間がかかると感じていますが、グリーンスレッドが使用されているため、パフォーマンスが低下することはわかっています。

この質問 を見てください。たとえば、非同期に関する同じタイプの懸念があり、単一の回答はあまり役に立ちません。

3
spirc

私は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を使用するため、新しいThreadsは多く作成されません。 。

ThreadPoolを使用することは、すべてのAPIに当てはまるわけではありません。ほとんどは次のパターンに従います。

  1. ライブラリはTaskCompletionSourceを作成します。

  2. ライブラリは、通知を受け取る手段を設定します。コールバック、タイマー、メッセージなど...

  3. ライブラリは、受け取った通知に応じて、SetResultまたはSetExceptionTaskCompletionSourceを呼び出す通知に反応するコードを設定します。

  4. ライブラリは、外部システムを実際に呼び出します。

  5. ライブラリは_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であり、awaitasyncメソッドにしたい場合は、メソッドをasyncにする必要があります。

つまり、そうです、asyncの伝達は厄介です。解決策は、エントリポイントが不純(およびasync)であり、外部システムを処理し(不純な命令型シェル)、not asyncコード(純粋な機能コア)を呼び出し、シェルに戻って相互運用性を高めることです。そうすれば、すべてをasyncにする必要がなくなり、コード全体を不純にする必要もなくなります。外部システムを分離する場合(それらを交換しやすくし、テストを容易にするため)、おそらくすでにこれを行っています。

Go myfunction()と言ってスレッドを作成するgoのアプローチはそれほど時間のかかるものではないように感じますが、使用されている緑のスレッドによりパフォーマンスが低下することはわかっています。

awaitgoよりも入力するのに時間がかかると不満がありますか?それは「冗長」を説明しています。

非同期はマルチスレッドを意味しません。たとえば、Threadを待たずにハードディスクを読み取ることができます。読み取るバッファーを初期化し、オペレーティングシステムに、ハードディスクに(DMAを介して)バッファーに書き込むように指示し、イベントをトリガーするようにドライバーに指示するよう指示します。次に、イベントで_TaskCompletionSource.SetResult_を実行すると、システムは継続をスケジュールできます(awaitの後のコード)。 Threadはありません。心配する必要はありませんでした。 そして、はい、それは私が上記で説明したのと同じパターンです。


コンパイラの舞台裏で何が起こっているのかを推測するのは難しいと思います

コンパイラは継続としてコードを書き換えています。ステートマシンです。コンパイラがコードをステートマシンとして書き換えるのはこれが初めてではありません。 コルーチン _yield return_のイテレータ。

asyncメソッドの場合、各awaitは、コードがその後に続くTaskが続くことを意味します。

Threadの呼び出しは待機しないことに注意してください。代わりに、asyncメソッドは最初のTaskをスケジュールし、その継続を設定し、メソッドに対して作成されたすべての継続が完了すると完了するTaskを返します。

実際には、エラー処理があり、_try-catch-finally_で動作するため、少し複雑です。コンパイラが書き換えを行います。

これはおそらく、スケジュールされたThreadsを実行するために使用できる場合は、呼び出し元のTaskが最終的に自由に作業できることを意味します。継続は、それを呼び出したのと同じThreadで実行される場合もあれば、別のもので実行される場合もあります。 TaskSchedulerまでです。デフォルトのTaskSchedulerThreadPoolを使用します。 そして、はい、カスタムTaskSchedulerを作成できます。


おそらくインタビュー Mads Torgersen:Inside C#Async はさらに理解を深めるのに役立ちます。強く推奨する。

10
Theraot

Webサーバーとその他のI/Oに高度にバインドされたアプリケーションのみを扱う場合は、非同期のものが多く、なくてはならないことを確認してください。ただし、IO非同期がそれほど有益ではなく、頻繁に使用されない場所にバインドされていないデスクトップおよびモバイルデバイス用のソフトウェアを作成している人はまだたくさんいます。

いずれにせよ、async/awaitは多くの人に共通の問題を解決する便利なツールであるため、多くのライブラリで使用されています。誰かがLINQやGenericsを使い始めたとき、またはC++ではなくC#を使い始めたときに、誰かが同じ質問をしたかもしれません。これは基本的に、アセンブリですべてを記述しないのと同じ理由です。

2
whatsisname

Webサーバーへの着信が100件あるとします。 100個すべてがSQLクエリの実行を開始します。呼び出しの99%は、SQLが結果を返すのを待つのに費やされています。

SQLが動作している間にスレッドをプールに戻し、別の要求を処理する方が良いでしょうか?

タスクを使用することで、UIコードを活用することもできます。 Unity 3DやCaliburn Microなどのタスクツールの前は、コルーチンを使用して非同期コードを同期的に実行していました。メインスレッド以外のスレッドではUIコンポーネントを操作できないため、これが必要です。また、非同期に実行しないと、UIが応答を停止します。 winformsでは、これを自分でマーシャリングする必要がありました。

1
Anders