web-dev-qa-db-ja.com

async-awaitを使用すると、パフォーマンス上の利点が得られますか?

async-_awaitについて読むたびに、ユースケースの例はalwaysで、フリーズしたくないUIがあります。すべてのプログラミングの本/チュートリアルが同じであるか、開発者として知っておくべきasync-_awaitの唯一のケースはUIブロックです。

async-awaitを使用してアルゴリズムのパフォーマンス上の利点を引き出す方法の例はありますか?同様に、古典的なプログラミングインタビューの質問を取り上げましょう。

  • 二分木で最も近い共通の祖先を見つける
  • 与えられたa[0]a[1]、...、a[n-1] 10進数の数字を表し、同じ数字を使用する次に高い数字を見つけます
  • 2つの並べ替えられた配列の中央値を見つける(つまり、それらをマージする場合は中央値)
  • 番号の配列12、...、n 1つの数値が欠落している、欠落している数値を見つける
  • 配列内の最大の2つの数値を見つける

async-awaitを使用してパフォーマンスを向上させる方法はありますか?もしそうなら、プロセッサが1つしかない場合はどうでしょうか?次に、マシンは実際に同時にタスクを実行するのではなく、単にタスク間で時間を分割しませんか?

22
user6048670

このインタビューでは、エリックリッパートは非同期待ちを朝食を作るコックと比較しました 。 async-awaitの利点を理解するのに大いに役立ちました。中央のどこかで「async-await」を検索してください

料理人が朝食を作らなければならないとします。彼はパンを焼いて卵をeggsでなければなりません。

方法1:同期。 1つのスレッドによって実行されます。パンを乾杯し始めます。パンが乾くまで待ちます。パンを削除します。お湯を沸かし始め、水が沸騰するまで待ち、卵を入れます。卵の準備ができるまで待って、卵を取り出します。お茶の水を沸かし始めます。水が沸騰するまで待ち、お茶を入れます。

すべての待機が表示されます。スレッドが待機している間、他のことを行うことができます。

方法2:非同期待機、まだ1つのスレッドパンの乾杯を開始します。パンが焼かれている間、卵とお茶のために水を沸かし始めます。それからあなたは待ち始めます。 3つのタスクのいずれかが完了したら、最初に完了したタスクに応じて、タスクの2番目の部分を実行します。したがって、卵の水が最初に沸騰したら、卵を調理し、タスクが完了するまで再び待ちます。

この説明では、1人(あなた)だけがすべての作業を行っています。関与するスレッドは1つだけです。良いことは、処理を行うスレッドが1つしかないため、コードがリーダーに対して非常に同期しているように見え、変数をスレッドセーフにする必要があまりないことです。

このようにすると、朝食が短時間で準備できるようになります(そして、パンはまだ温かいです!)。コンピューターの生活では、ファイルをディスクに書き込む、データベースまたはインターネットから情報を取得するなど、スレッドが別のプロセスの終了を待たなければならないときにこれらのことが起こります。これらは通常、WriteWriteAsyncReadReadAsyncの非同期バージョンが表示される種類の関数です。

追加:他のユーザーからのいくつかの発言、およびいくつかのテストの後、実際に待機してから作業を続行するスレッドであることがわかった。この他のスレッドは同じ「コンテキスト」を持っているため、元のスレッドのように動作できます。

方法3:料理人を雇って、お茶を入れながらパンを焼いて卵をゆでる:本物の非同期。複数のスレッドこれは、個別のスレッドの作成を伴うため、最も高価なオプションです。朝食を作る例では、プロセスの比較的長い時間はとにかく何もしていないので、これはおそらくプロセスをあまりスピードアップしません。ただし、たとえばトマトもスライスする必要がある場合は、async-awaitを使用して他の処理を行っている間に料理人(別のスレッド)にこれを行わせると便利です。もちろん、あなたがするのは、料理人が彼のスライスを完了するのを待つことです。

多くを説明する別の記事は Async and Await で、これまでに非常に役立つStephen Clearyによって書かれました。

40

Async-awaitについて読むたびに、ユースケースの例は常に、フリーズしたくないUIがある例です。

これは、asyncの最も一般的な使用例です。もう1つはサーバー側アプリケーションで、asyncを使用するとWebサーバーのスケーラビリティが向上します。

Async-awaitを使用してアルゴリズムのパフォーマンス上の利点を引き出す方法の例はありますか?

番号。

並列処理を行いたい場合は、タスク並列ライブラリを使用できます。並列処理とは、システム内の複数のコア間でアルゴリズムの一部を分割する複数のスレッドの使用です。並列処理は並行性の1つの形式です(同時に複数のことを行います)。

非同期コードはまったく異なります。非同期コードのポイントは、not操作の進行中に現在のスレッドを使用することです。非同期コードは通常、I/Oバインドまたはイベント(タイマーなど)に基づいています。非同期コードは並行性の別の形式です。

私のブログには async intro と、 asyncがスレッドを使用しない方法 の投稿があります。

タスク並列ライブラリで使用されるタスクは、スレッド上でスケジュールでき、コードを実行することに注意してください。タスクベースの非同期パターンで使用されるタスクにはコードがなく、「実行」されません。両方のタイプのタスクは同じタイプ(Task)で表されますが、作成と使用方法はまったく異なります。これらについて デリゲートタスクと約束タスク をブログで詳しく説明します。

18
Stephen Cleary

短くて非常に一般的な場合-いいえ、通常はそうではありません。しかし、「パフォーマンス」は多くの方法で理解できるので、それはもう少し言葉を必要とします。

「ジョブ」がI/Oにバインドされている場合のみ、非同期/待機「時間を節約」。 CPUにバインドされているジョブにそれを適用すると、パフォーマンスが低下します。 CPUで10秒かかる計算がある場合、async/awaitを追加すると(つまり、タスクの作成、スケジューリング、同期)、その10秒にXの余分な時間が追加されるため、まだ書き込みが必要です。 CPUを使用してジョブを完了します。アムダール法の考え方に近いもの。そうでもないが、かなり近い。

ただし、いくつかの「しかし..」があります。

まず、async/awaitの導入によるパフォーマンスの低下はそれほど大きくありません。 (特に、無理をしないように注意している場合)。

2つ目は、async/awaitを使用するとI/Oインターリーブコードをはるかに簡単に記述できるため、怠けすぎて(/)がそうでない場所でI/Oの待機時間を削除する新しい機会に気付くか、 async/await構文の良さなしにコードを追跡しにくくする場所。たとえば、ネットワーク要求を中心にコードを分割することはかなり明白ですが、CSVファイルを書き込んだり、構成ファイルを読み込んだりするいくつかの場所でファイルI/Oをアップグレードすることもできます。ここでのゲインはasync/awaitのおかげではないこと-ファイルI/Oを処理するコードを書き直したことのおかげです。あなたも非同期/待機せずにそれを行うことができます。

第三に、一部のI/O操作が簡単であるため、CPU集中型の作業を別のサービスまたはマシンにオフロードする方がはるかに簡単であることに気付く場合があります。これにより、知覚パフォーマンスも向上します(「時計時間」の短縮)リソース消費が増加します。別のマシンを追加したり、ネットワーク操作に時間を費やしたりなど。

4番目:UI。あなたは本当にそれを凍結したくありません。 I/OバウンドとCPUバウンドの両方のジョブをタスクにラップし、それらを非同期/待機して、UIの応答性を維持するのは非常に簡単です。だから、どこでもそれが言及されているのを見ます。ただし、I/Oバウンドopは理想的には非常に葉まで非同期であり、すべての長いI/Oでアイドル待機時間をできるだけ削除する必要がありますが、CPUバウンドジョブは1つ以上を分割または非同期化する必要はありませんレベルダウン。巨大なモノリシック計算ジョブを1つのタスクにラップするだけで、UIのブロックを解除できます。もちろん、プロセッサ/コアが多数ある場合、内部で可能なことはすべて並列化する価値がありますが、I/Oとは対照的に、分割が多すぎると、計算を噛む代わりにタスクを切り替えるのに忙しくなります。

要約:時間のかかるI/Oがある場合は、非同期操作で多くの時間を節約できます。非同期のI/O操作をやり直すのは困難です。 CPUを使用する操作がある場合は、何かを追加するとCPU時間とメモリが合計で消費されますが、ジョブをより多くのコアで同時に実行できる小さな部分に分割することで、壁時計時間を改善できます時間。無理をするのは難しくないので、少し注意する必要があります。

6
quetzalcoatl

ほとんどの場合、スケーラビリティのように直接的なパフォーマンス(実行中のタスクはより高速に、および/またはより少ないメモリで実行されます)で得られません。より少ないスレッドを使用して同じ数の同時タスクを実行すると、同時に実行できる同時タスクの数が多くなります。

したがって、ほとんどの場合、特定の操作のパフォーマンスが向上することはありませんが、頻繁に使用するとパフォーマンスが向上することがわかります。

操作が、真に非同期なもの(複数の非同期I/O)を伴う並列タスクを必要とする場合、そのスケーラビリティはその単一の操作に利益をもたらします。スレッドで発生するブロッキングの程度が低下するため、マシンが現在待機していないタスク間でのみ時間を分割するため、コアが1つしかない場合でもこれが発生します。

これは、(タスクを使用して実行されるかどうかに関係なく)使用可能なコアの数までしかスケールアップしない並列CPUバインド操作とは異なります。 (ハイパースレッドコアは、いくつかの点で2つ以上のコアのように動作し、他の点では動作しません)。

3
Jon Hanna

メソッドは現在の同期コンテキストで実行され、メソッドがアクティブな場合にのみスレッドの時間を使用します。 Task.Runを使用してCPUにバインドされた作業をバックグラウンドスレッドに移動できますが、バックグラウンドスレッドは、結果が利用可能になるのを待っているプロセスには役立ちません。

アプリケーションに1つのCPUと複数のスレッドがある場合、CPUはスレッドを切り替えて並列処理をシミュレートします。 async/awaitを使用すると、非同期操作にスレッド時間が必要なくなります。したがって、アプリケーションの他のスレッドがジョブを実行するためにより多くの時間を与えることができます。たとえば、アプリケーション(非UI)は引き続きHTTP呼び出しを行うことができ、必要なのは応答を待つだけです。これは、async/awaitを使用するメリットが大きい場合の1つです。

async DoJobAsync()を呼び出すときは、.ConfigureAwait(false)を忘れずに、UIスレッドコンテキストにマージする必要のない非UIアプリのパフォーマンスを向上させてください。

コードをきれいに保つのに役立つニース構文については言及しません。

[〜#〜] msdn [〜#〜]

2
Andrei

Asyncおよびawaitキーワードは、追加のスレッドを作成しません。非同期メソッドは、独自のスレッドで実行されないため、マルチスレッドを必要としません。メソッドは現在の同期コンテキストで実行され、メソッドがアクティブな場合にのみスレッドの時間を使用します。 Task.Runを使用してCPUにバインドされた作業をバックグラウンドスレッドに移動できますが、バックグラウンドスレッドは、結果が利用可能になるのを待っているプロセスには役立ちません。

.NETのasync-await機能は、他のフレームワークの機能と違いはありません。ローカル計算ではパフォーマンス上の利点はありませんが、1つのタスクがスレッドをブロックするのではなく、1つのスレッドでタスク間の連続的な切り替えを許可するだけです。ローカル計算のパフォーマンスを向上させるには、タスク並列ライブラリを使用します。

訪問 https://msdn.Microsoft.com/en-us/library/dd460717(v = vs.110).aspx

1
matt-sungwook