私はasync / awaitをいつ読んでいたかTask.Yield
役に立つかもしれませんし、出会ったこの郵便受け。私はその記事から以下に関して質問がありました:
あなたがasync / awaitを使うとき、その方法があなたに保証されるという保証はありません。 待っているときに電話をかける
FooAsync()
実際には非同期に実行されます。 内部実装は完全にを使って自由に戻ることができます 同期パス
おそらく私の頭の中で非同期の定義が並んでいないので、これは私には少し不明瞭です。
私の考えでは、私は主にUI開発を行っているので、非同期コードはUIスレッド上では実行されず、他のスレッド上では実行されないコードです。私が引用したテキストで推測しているように、メソッドがどのスレッドでブロックされても(たとえばスレッドプールスレッドであっても)、メソッドは本当に非同期ではありません。
質問:
私がCPUに縛られている長時間実行されているタスクを持っているなら(それがたくさんの難しい数学をしているとしよう)、それからそのタスクを非同期的に実行することはいくつかのスレッドを正しくブロックしなければなりませんか?何かが実際に数学をしなければなりません。私がそれを待っているなら、それからいくつかのスレッドはブロックされています。
真に非同期的なメソッドの例は何ですか?また、それらは実際にどのように機能しますか?ハードウェア機能を利用してスレッドがブロックされることのないI / O操作に限定されていますか。
おそらく私の頭の中で非同期の定義が並んでいないので、これは私には少し不明瞭です。
説明を求めてくれてありがとう。
私の考えでは、私は主にUI開発を行っているので、非同期コードはUIスレッド上では実行されず、他のスレッド上では実行されないコードです。
その信念は一般的ですが偽です。非同期コードが任意の2番目のスレッドで実行されるという要件はありません。
あなたが朝食を作っていると想像してください。あなたはトースターにトーストを入れて、トーストが飛び出すのを待っている間に、昨日からあなたのメールを見て、いくつかの請求書を払い、そしてちょっと、トーストが飛び出しました。あなたはその請求書の支払いを終えてから、あなたのトーストを飲みます。
あなたのトースターを見るためにどこであなたは2人目の労働者を雇いましたか?
あなたはしませんでした。糸は労働者です。非同期ワークフローは1つのスレッドですべて発生する可能性があります。非同期ワークフローのポイントは、避けるあなたがそれを避けることができるならばもっと多くの労働者を雇う。
私がCPUに縛られている長時間実行されているタスクを持っているなら(それがたくさんの難しい数学をしているとしよう)、それからそのタスクを非同期的に実行することはいくつかのスレッドを正しくブロックしなければなりませんか?何かが実際に数学をしなければなりません。
ここで、私はあなたに解決するのが難しい問題をあげます。これは100の数字の列です。手でそれらを合計してください。それで、あなたは最初のものを2番目のものに加えて、合計を作ります。それから3番目に現在の合計を加え、合計を得ます。それで、ああ、地獄、2ページ目の数字がなくなっています。あなたがどこにいたか覚えていて、そして乾杯してください。ああ、乾杯が乾杯している間に、手紙が残りの数字と共に届いた。トーストのバター焼きが終わったら、続けてこれらの数字を足し合わせ、次に自由な時間があるときはトーストを食べることを忘れないでください。
番号を追加するためにあなたが他の労働者を雇った部分はどこですか?計算量の多い作業は同期的である必要はなく、スレッドをブロックする必要もありません。。計算作業を潜在的に非同期にすることは、それを停止し、あなたがいた場所を覚えていて、何か他のことをし、何をすべきか覚えていることですその後そして中断したところから再開します。
今では確かに可能何もしないで番号を追加してから解雇される2人目の労働者を雇うため。そして、あなたはその労働者に「あなたはやりましたか?」と尋ねることができます。答えが「いいえ」の場合は、サンドイッチを作り終えるまでサンドイッチを作ることができます。そのようにあなたと労働者の両方は忙しいです。しかし、ありません要件その非同期は複数の作業者を含みます。
私がそれを待っているなら、それからいくつかのスレッドはブロックされています。
ダメダメダメ。これはあなたの誤解の最も重要な部分です。await
「このジョブを非同期的に開始する」という意味ではありません。await
「ここには非同期で生成された結果があり、利用できない可能性があります。利用できない場合は、このスレッドでやるべき他の作業を見つけるだから私たちはではないスレッドをブロックしています。待って反対側あなたが言ったことのうち。
真に非同期的なメソッドの例は何ですか?また、それらは実際にどのように機能しますか?ハードウェア機能を利用してスレッドがブロックされることのないI / O操作に限定されていますか。
非同期作業にはカスタムハードウェアまたは複数のスレッドが含まれることがよくありますが、そうである必要はありません。
考えてはいけない労働者。について考えるワークフロー。非同期の本質はワークフローを細かく分割するそのようなあなたはそれらの部分が起こるべき順序を決定することができます、 その後各部分を順番に実行するしかし、互いに依存関係を持たない部分をインターリーブできるようにする。
非同期ワークフローでは、簡単に検出できますパーツ間の依存関係が表現されているワークフロー内の場所。そのような部分はでマークされていますawait
。それがの意味ですawait
以下のコードは、ワークフローのこの部分が完了しているかどうかによって異なるため、完了していない場合は、実行する他のタスクを見つけて、タスクが完了したら後でここに戻ってきてください。全体的な要点は、将来必要な結果が生み出されている世界でさえ、労働者を働かせ続けることです。
Task
抽象化です。 - Euphoric
async / awaitを読んでいました
私は私をお勧めしますasync
イントロ?
そしてTask.Yieldが役に立つかもしれないとき
ほとんどは決してない。私はユニットテストをするときそれが時々役に立つと思います。
私の考えでは、私は主にUI開発を行っているので、非同期コードはUIスレッド上では実行されず、他のスレッド上では実行されないコードです。
私が引用したテキストで推測しているように、メソッドがどのスレッドでブロックされても(たとえばスレッドプールスレッドであっても)、メソッドは本当に非同期ではありません。
私はそれが正しいと言うでしょう。どのスレッドもブロックしない(そして同期的でない)操作には、「真に非同期」という用語を使用します。また、「偽の非同期」という用語を使用することもあります。現れる非同期ですが、スレッドプールthreadを実行したりスレッドプールをブロックしたりするので、そのようにのみ動作します。
私がCPUに縛られている長時間実行されているタスクを持っているなら(それがたくさんの難しい数学をしているとしよう)、それからそのタスクを非同期的に実行することはいくつかのスレッドを正しくブロックしなければなりませんか?何かが実際に数学をしなければなりません。
はい;この場合は、同期APIを使用してその作業を定義し(同期作業なので)、それを使用してUIスレッドから呼び出すことができます。Task.Run
例えば、
var result = await Task.Run(() => MySynchronousCpuBoundCode());
私がそれを待っているなら、それからいくつかのスレッドはブロックされています。
いいえ。スレッドプールスレッドはコードを実行するために使用され(実際にはブロックされない)、UIスレッドはそのコードが完了するのを非同期的に待っています(ブロックされていません)。
真に非同期的なメソッドの例は何ですか?また、それらは実際にどのように機能しますか?
NetworkStream.WriteAsync
(間接的に)ネットワークカードに数バイトの書き込みを要求します。バイトを一度に1つずつ書き出し、各バイトが書き込まれるのを待つ責任を負うスレッドはありません。ネットワークカードがそれをすべて処理します。ネットワークカードがすべてのバイトの書き込みを完了すると、(最終的に)から返されたタスクを完了します。WriteAsync
。
ハードウェア機能を利用してスレッドがブロックされることのないI / O操作に限定されていますか。
入出力操作は簡単な例ですが、完全ではありません。もう1つの非常に簡単な例はタイマーです(例:Task.Delay
)ただし、あらゆる種類の「イベント」を中心にして、本当に非同期のAPIを構築することもできます。
Task.Yield
非同期を強制します、はい。しかし、いつこれを行う必要がありますか?ほとんどは決してない。単体テストを行うときに便利です。本番コードで必要と思われる場合は、本番コードの設計を詳細に検討し、どのような改善が可能かを確認してください。 - Stephen ClearyTask.Yield
メッセージキューがWindows上で機能する方法のため、そのためには機能しません。 - Stephen Cleary
async / awaitを使用するとき、FooAsync()を待つときに呼び出すメソッドが実際に非同期的に実行されるという保証はありません。内部実装は完全同期パスを使用して自由に戻ることができます。
これは私には少し不明瞭です。 私の頭の中で非同期は並んでいません。
これは単に、非同期メソッドを呼び出すときに2つのケースがあることを意味します。
1つは、タスクをあなたに返した時点で、操作はすでに完了しているということです。これは同期パスです。 2つ目は、操作がまだ進行中であるということです - これは非同期パスです。
このコードを考えてみてください。これらのパスの両方が示されているはずです。キーがキャッシュにある場合は、同期的に返されます。それ以外の場合は、データベースへの呼び出しを行う非同期操作が開始されます。
Task<T> GetCachedDataAsync(string key)
{
if(cache.TryGetvalue(key, out T value))
{
return Task.FromResult(value); // synchronous: no awaits here.
}
// start a fully async op.
return GetDataImpl();
async Task<T> GetDataImpl()
{
value = await database.GetValueAsync(key);
cache[key] = value;
return value;
}
}
それを理解することで、理論的には、database.GetValueAsync()
似たようなコードを持っていて、それ自体が同期的に戻ることができるかもしれません。きみの非同期パスは、100%同期して実行される可能性があります。しかし、コードは気にする必要はありません。async / awaitは両方のケースをシームレスに処理します。
私がCPUに縛られている長時間実行されているタスクを持っているなら(それがたくさんの難しい数学をしているとしよう)、それからそのタスクを非同期的に実行することはいくつかのスレッドを正しくブロックしなければなりませんか?何かが実際に数学をしなければなりません。私がそれを待っているなら、それからいくつかのスレッドはブロックされています。
ブロッキングは明確に定義された用語です。つまり、スレッドが何か(I / O、ミューテックスなど)を待っている間に実行ウィンドウが生成されたことを意味します。したがって、数学を実行しているスレッドはブロックされているとは見なされません。実際に作業を実行しています。
真に非同期的なメソッドの例は何ですか?また、それらは実際にどのように機能しますか?ハードウェア機能を利用してスレッドがブロックされることのないI / O操作に限定されていますか。
「真に非同期な方法」は単純にブロックしない方法です。それは通常I / Oを含むことになりますが、それはまた意味することができますawait
現在のスレッドに別のものを使用したい場合(UI開発の場合のように)、または並列処理を導入しようとしている場合は、重い数学コードを入力します。
async Task<double> DoSomethingAsync()
{
double x = await ReadXFromFile();
Task<double> a = LongMathCodeA(x);
Task<double> b = LongMathCodeB(x);
await Task.WhenAll(a, b);
return a.Result + b.Result;
}
このトピックはかなり広大であり、いくつかの議論が生じるかもしれません。ただし、async
そしてawait
C#では、非同期プログラミングと見なされます。しかし、非同期性がどのように機能するかについてはまったく異なる議論です。 .NET 4.5までは、asyncやawaitというキーワードはなく、開発者たちは直接に対抗して開発しなければなりませんでした。タスクパラレルライブラリy(TPL)そこで開発者はいつ、どのようにして新しいタスクやスレッドを作成するかを完全に制御することができました。ただし、このトピックに関する専門家ではないため、アプリケーションがスレッド間の競合状態などによる深刻なパフォーマンス上の問題やバグを抱えている可能性があります。
.NET 4.5からは、非同期プログラミングへの新しいアプローチとともに、asyncキーワードとawaitキーワードが導入されました。 asyncキーワードとawaitキーワードによって追加のスレッドが作成されることはありません。非同期メソッドは独自のスレッドでは実行されないため、非同期メソッドはマルチスレッドを必要としません。このメソッドは現在の同期コンテキストで実行され、メソッドがアクティブな場合にのみスレッドの時間を使用します。あなたが使用することができますTask.Run
しかし、バックグラウンドスレッドは、結果が利用可能になるのを待っているだけのプロセスには役立ちません。
非同期プログラミングに対する非同期ベースのアプローチは、ほとんどすべての場合において既存のアプローチよりも望ましいです。特に、この方法は、コードが単純で、競合状態から保護する必要がないため、IOバウンド操作ではBackgroundWorkerよりも優れています。このトピックについてもっと読むことができますここに。
私は自分自身をC#の黒帯とは考えておらず、経験豊富な開発者も議論を重ねるかもしれませんが、原則として私はあなたの質問に答えられることを願っています。
非同期は並行性のみを意味します。実際、明示的なスレッドを使用しても、それらが同時に実行されることを保証するものではありません(たとえば、スレッドが同じシングルコアに親和性がある場合、またはより一般的には最初のマシンにコアが1つしかない場合)。
したがって、非同期操作が他のものと同時に発生するとは思わないでください。非同期はそれが起こることを意味します別の時に(ある(ギリシャ語)=なし、シン(ギリシャ語)=一緒に、クロノス(ギリシャ語)=時間=>非同期=同時に起こっていない)。
注:非同期性の概念は、呼び出し時にコードが実際に実行されるタイミングを気にしないということです。これにより、可能であれば、システムは並列処理を利用して操作を実行できます。それはすぐに走るかもしれません。それは同じスレッド上でさえ起こり得ます…それについては後でもっと詳しく説明します。
あなたがawait
非同期操作は、あなたが作成している並行性(と(ラテン語)=一緒に、ラン(ラテン)=実行=> "同時" =一緒に走る)これは、先に進む前に非同期操作が完了するように要求しているためです。実行は収束したと言えます。これは、スレッドを結合するという概念に似ています。
async / awaitを使用するとき、FooAsync()を待つときに呼び出すメソッドが実際に非同期的に実行されるという保証はありません。内部実装は完全同期パスを使用して自由に戻ることができます。
これは3つの方法で起こる可能性があります。
使用できますawait
返されるものすべてにTask
。あなたが受け取るときTask
それはすでに完成しているかもしれません。
それでも、それだけで同期的に実行されたわけではありません。実際、それは非同期的に実行され、あなたがTask
インスタンス。
できることを覚えておいてくださいawait
既に完了したタスクについて
private static async Task CallFooAsync()
{
await FooAsync();
}
private static Task FooAsync()
{
return Task.CompletedTask;
}
private static void Main()
{
CallFooAsync().Wait();
}
また、async
メソッドはありませんawait
同期的に実行されます。
注:すでにご存知のように、Task
ネットワークやファイルシステムなどで待機している可能性があります。Thread
またはで何かをエンキューするThreadPool
。
シングルスレッドによって処理される同期コンテキストでは、結果は次のようになります。Task
同期的に、いくらかのオーバーヘッドを伴います。これはUIスレッドの場合です、私は以下で何が起こるかについてもっと話します。
カスタムを書くことは可能ですTaskScheduler常にタスクを同期的に実行するため。同じスレッドで、それは呼び出しを行います。
注:最近私はカスタムを書いたSyncrhonizationContext
それはシングルスレッド上でタスクを実行します。あなたはそれを見つけることができます(System.Threading.Tasks。)タスクスケジューラを作成する。それはそのようになるだろうTaskScheduler
への呼び出しでFromCurrentSynchronizationContext
。
デフォルトTaskScheduler
への呼び出しをエンキューします。ThreadPool
。まだあなたが操作を待っているとき、それが上で実行されていない場合ThreadPool
それはからそれを削除しようとしますThreadPool
そしてそれをインラインで実行します(待機しているのと同じスレッドで...スレッドはとにかく待機しているのでビジーではありません)。
注:注目すべき例外の1つはTask
でマークLongRunning
。LongRunning
Task
sは別のスレッドで実行されます。
私がCPUに縛られている長時間実行されているタスクを持っているなら(それがたくさんの難しい数学をしているとしよう)、それからそのタスクを非同期的に実行することはいくつかのスレッドを正しくブロックしなければなりませんか?何かが実際に数学をしなければなりません。私がそれを待っているなら、それからいくつかのスレッドはブロックされています。
あなたが計算をしているなら、それらはあるスレッドで起こらなければなりません、その部分は正しいです。
それでも、の美しさasync
そしてawait
待機しているスレッドをブロックする必要がないということです(後で詳しく説明します)。それでも、待機しているタスクを待機しているのと同じスレッドで実行するようにスケジュールし、同期実行を行うことで非常に簡単に自分自身を撃つことができます(これはUIスレッドでは簡単な間違いです)。
の主な特徴の1つasync
そしてawait
彼らはSynchronizationContext
呼び出し元から。デフォルトを使用することになるほとんどのスレッドでTaskScheduler
(前述のように、ThreasPool
)ただし、UIスレッドの場合、タスクをメッセージキューにポストすることを意味します。つまり、タスクはUIスレッド上で実行されます。これの利点はあなたが使用する必要がないということです。Invoke
またはBeginInvoke
UIコンポーネントにアクセスします。
私がする方法に入る前にawait
あるTask
ブロックせずにUIスレッドから、私はそれを実装することが可能であることに注意したいですTaskScheduler
どこならawait
にTask
自分のスレッドをブロックしたりアイドル状態にしたりしないでください。代わりに、自分のスレッドに別のスレッドを選択させます。Task
それは実行を待っています。私が...だったとき.NET 2.0のバックポートタスク私はこれを試しました。
真に非同期的なメソッドの例は何ですか?また、それらは実際にどのように機能しますか?ハードウェア機能を利用してスレッドがブロックされることのないI / O操作に限定されていますか。
あなたは混乱しているようです非同期とスレッドをブロックしていない。あなたが欲しいものがスレッドをブロックすることを必要としない.NETの非同期操作の例であるならば、あなたが握りやすいと思うかもしれないそれをする方法は使用することです続きの代わりにawait
。そして、あなたがUIスレッド上で実行する必要がある継続のために、あなたは使うことができますTaskScheduler.FromCurrentSynchronizationContext
。
派手なスピン待ちを実装しない。そしてそれによって私はTimer
、Application.Idle
それとも何か。
使うときasync
あなたはそれを破ることができるような方法でメソッドのコードを書き直すようにコンパイラに言っています。結果は継続と似ていますが、構文がはるかに便利です。スレッドがawait
のTask
スケジュールされます、そしてスレッドは現在の後に自由に続けることができます。async
呼び出し(メソッド外)ときにTask
されている(続きの後)await
) 計画されている。
UIスレッドの場合、これは一度到達したことを意味します。await
メッセージを処理し続けることは自由です。一度待ったTask
されている(続きの後)await
)予定されます。その結果、await
スレッドをブロックするわけではありません。
まだ盲目的に追加async
そしてawait
あなたの問題をすべて解決するわけではありません。
私はあなたに実験を提出します。新しいWindowsフォームアプリケーションを入手するButton
そしてTextBox
次のコードを追加します。
private async void button1_Click(object sender, EventArgs e)
{
await WorkAsync(5000);
textBox1.Text = @"DONE";
}
private async Task WorkAsync(int milliseconds)
{
Thread.Sleep(milliseconds);
}
それはUIをブロックします。何が起こるかというと、先に述べたように、await
自動的にSynchronizationContext
呼び出し元スレッドのこの場合、それがUIスレッドです。したがって、WorkAsync
UIスレッド上で実行されます。
これが起こるのです:
await WorkAsync(5000)
WorkAsync(5000)
(そしてその継続をスケジュールする)現在の同期コンテキストで実行するようにスケジュールされます。それはUIスレッド同期コンテキストです…それはそれを実行するためにメッセージをポストすることを意味しますWorkAsync(5000)
そしてその継続をスケジュールWorkAsync(5000)
継続してWorkAsync
、UIスレッドが実行されます。Thread.Sleep
。 UIは5秒間反応しなくなりました。その結果、オーバーヘッドのある同期実行になります。
はい、あなたは使うべきですTask.Delay
代わりに。それはポイントではありません。考えてSleep
いくつかの計算を代用します。ポイントはただ使うだけですasync
そしてawait
どこでもあなたに自動的に並列であるアプリケーションを与えることはありません。バックグラウンドスレッドで何を実行したいのかを選択することをお勧めします。ThreadPool
そして、あなたは何をUIスレッドで実行したいですか。
それでは、次のコードを試してください。
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => Work(5000));
textBox1.Text = @"DONE";
}
private void Work(int milliseconds)
{
Thread.Sleep(milliseconds);
}
待ってもUIがブロックされないことがわかります。これは、この場合ですThread.Sleep
今で実行されていますThreadPool
おかげでTask.Run
。そしてありがとうbutton1_Click
であることasync
コードが到達したらawait
UIスレッドは自由に機能し続けます。後にTask
完了すると、コードはawait
それを可能にするためにコンパイラがメソッドを書き換えたおかげで。
これが起こるのです:
await Task.Run(() => Work(5000))
Task.Run(() => Work(5000))
(そしてその継続をスケジュールする)現在の同期コンテキストで実行するようにスケジュールされます。それはUIスレッド同期コンテキストです…それはそれを実行するためにメッセージをポストすることを意味しますTask.Run(() => Work(5000))
そして終了したら継続を予定しなさいTask.Run(() => Work(5000))
継続して、これは上で実行されますThreadPool
ときにThreadPool
完了すると、継続によって、クリックイベントハンドラの残りの部分が実行されるようにスケジュールされます。これは、UIスレッドに別のメッセージを投稿することによって行われます。 UIスレッドがclickイベントハンドラで続行するメッセージを選択すると、テキストボックスが更新されます。
スティーブンの答えはすでに素晴らしいので、私は彼が言ったことを繰り返すつもりはない。私はスタックオーバーフロー(そして他の場所で)で同じ議論を何度も繰り返すことについて私の公正な分担をしました。
代わりに、非同期コードに関する1つの重要な抽象的事項に焦点を当ててみましょう。これは絶対修飾子ではありません。コードの一部が非同期であると言っても意味がありません - それは常に非同期です他の何かに関して。これは非常に重要です。
の目的await
非同期操作といくつかの関連する同期コードの上に同期ワークフローを構築することです。あなたのコード現れる完全に同期1コード自体に。
var a = await A();
await B(a);
イベントの順序は次のように指定されます。await
呼び出しBはAの戻り値を使用します。つまり、AはBの前に実行されている必要があります。このコードを含むメソッドは同期ワークフローを持ち、2つのメソッドAとBは互いに同期しています。
同期ワークフローは通常、より簡単に考えることができ、さらに重要なことに、多くのワークフローは単純に考えることができるので、これは非常に便利です。あります同期的です。実行するためにBがAの結果を必要とする場合、しなければならないAの後に実行2。別のHTTPリクエストのURLを取得するためにHTTPリクエストを行う必要がある場合は、最初のリクエストが完了するのを待つ必要があります。スレッド/タスクスケジューリングとは関係ありません。注文する必要のないものに注文を強制する「偶発的な同期」とは別に、おそらくこの「固有の同期」と呼ぶことができます。
あなたは言う:
私の考えでは、私は主にUI開発を行っているので、非同期コードはUIスレッド上では実行されず、他のスレッド上では実行されないコードです。
あなたは、UIに関して非同期的に実行されるコードを記述しています。これは確かに非同期にとって非常に便利なケースです(人々は応答を停止するUIが好きではありません)。しかし、それはより一般的な原則の単なる特定のケースです - 物事が互いに関して順不同で起こることを可能にします。これも絶対ではありません一部順番がずれているイベント(ユーザーがウィンドウをドラッグしたとき、またはプログレスバーが変更されたときに、ウィンドウを再描画する必要がある場合など)ではない順番が違っている(Loadアクションが終了する前にProcessボタンをクリックしてはいけない)。await
このユースケースではそうではありませんそれ使用とは異なりますApplication.DoEvents
原則として - それは同じ問題と利点の多くを紹介します。
これはまた、元の引用が面白くなる部分です。 UIを更新するにはスレッドが必要です。そのスレッドはイベントハンドラを呼び出します。await
。それはその行があるという意味ですかawait
ユーザー入力に応じてUIが自分自身を更新できるようにするために使用されるものいいえ
まず、それを理解する必要がありますawait
それがメソッド呼び出しであるかのように、その引数を使用します。私のサンプルでは、A
によって生成されたコードの前にすでに呼び出されている必要があります。await
「UIループに制御を戻す」など、何でもできます。の戻り値A
ですTask<T>
ただの代わりにT
「将来の可能性のある価値」を表す - そしてそしてawait
生成されたコードは、値がすでに存在するか(その場合は同じスレッドで継続するか)どうかを確認します(つまり、スレッドをUIループに解放します)。しかしどちらの場合も、Task<T>
価値そのものしなければならないから返されましたA
。
この実装を検討してください。
public async Task<int> A()
{
Thread.Sleep(1000);
return 42;
}
呼び出し側のニーズA
値を返す(intのタスク)。ないからawait
メソッド内のs、つまりreturn 42;
。しかし、2つの操作はスレッドに関して同期的であるため、スリープが終了する前にそれを行うことはできません。それが使用するかどうかに関係なく、呼び出し元のスレッドは1秒間ブロックされます。await
かどうか - ブロッキングが行われているA()
それ自体ではなくawait theTaskResultOfA
。
対照的に、これを考慮してください。
public async Task<int> A()
{
await Task.Delay(1000);
return 42;
}
実行がすぐにawait
待機中のタスクがまだ終了していないことがわかり、呼び出し元に制御が戻ります。そしてそのawait
その結果、呼び出し側では制御をその呼び出し元私たちはUIに関していくつかのコードを非同期にすることに成功しました。 UIスレッドとAの間の同時性は偶然のもので、私たちはそれを取り除きました。
ここで重要なのは、コードを調べずに2つの実装を外部から区別する方法がないということです。戻り値の型だけがメソッドシグネチャの一部です - メソッドが非同期的に実行されるとは言っていません。よろしく。これにはいくつかの正当な理由があるので、それを戦う意味はありません - 例えば、結果がすでに利用可能であるときに実行のスレッドを壊す意味がありません。
var responseTask = GetAsync("http://www.google.com");
// Do some CPU intensive task
ComputeAllTheFuzz();
response = await responseTask;
我々はいくつかの仕事をする必要があります。一部のイベントは他のイベントに対して非同期的に実行できます(この場合、ComputeAllTheFuzz
HTTP要求とは無関係で非同期です。しかし、ある時点で、同期ワークフローに戻る必要があります(たとえば、次の両方の結果を必要とするもの)。ComputeAllTheFuzz
そしてHTTPリクエスト)。それはawait
実行を再度同期させるポイント(複数の非同期ワークフローがある場合は、次のようにします。Task.WhenAll
)ただし、HTTP要求が計算の前に完了した場合、その時点で制御を解放しても意味がありません。await
要点 - 同じスレッドで単純に続行できます。 CPUを無駄にすることはありません - スレッドをブロックすることはありません。それは便利なCPUの仕事をします。しかし、UIが更新される機会はありませんでした。
これはもちろん、このパターンがより一般的な非同期メソッドで通常避けられる理由です。非同期コードの用途によっては(スレッドやCPU時間の浪費を避けるために)役立ちますが、そうでない場合(UIの応答性を保つため)は役に立ちます。そのような方法でUIの反応を良くすることを期待しているのであれば、結果に満足できないでしょう。しかし、たとえばWebサービスの一部として使用すると、うまく機能します。スレッドを無駄にすることを避け、UIの応答性を維持することではありません(既にサービスエンドポイントを非同期で呼び出すことによって提供されます)。サービス側でも同じことが言えます。
要するに、await
呼び出し側に関して非同期のコードを書くことができます。それは、魔法のような非同期性を生み出すのではなく、すべてに関して非同期ではなく、CPUの使用やスレッドのブロックを妨げることもありません。非同期操作から同期ワークフローを簡単に作成し、呼び出し元に対してワークフロー全体の一部を非同期として表示するためのツールを提供するだけです。
UIイベントハンドラを考えてみましょう。個々の非同期操作が実行するためにスレッドを必要としない場合(例えば非同期I / O)、非同期方法の一部は他のコードが元のスレッド上で実行することを可能にする(そしてUIはそれらの部分で応答し続ける)。操作が再びCPU /スレッドを必要とするとき、それは必要とするかもしれませんしないかもしれません。元の作業を継続するためのスレッド。もしそうであれば、CPU作業の間UIは再びブロックされます。そうでなければ(ウェイターこれを使ってこれを指定するConfigureAwait(false)
)、UIコードは並行して実行されます。もちろん両方を処理するのに十分なリソースがあると仮定します。 UIを常に反応させ続ける必要がある場合、目立つほど長く実行するためにUIスレッドを使用することはできません。たとえ信頼できない「通常は非同期ですが、場合によっては数秒間ブロックする」非同期をラップする必要があるとしてもです。メソッドTask.Run
。両方のアプローチにはコストと利点があります - すべてのエンジニアリングと同様に、これはトレードオフです。
これは、非同期/待機によって、コードが制御をブロックして別のフローに制御を解放し、その後制御を再開するがスレッドを必要としないことを示す非同期コードです。
public static async Task<string> Foo()
{
Console.WriteLine("In Foo");
await Task.Yield();
Console.WriteLine("I'm Back");
return "Foo";
}
static void Main(string[] args)
{
var t = new Task(async () =>
{
Console.WriteLine("Start");
var f = Foo();
Console.WriteLine("After Foo");
var r = await f;
Console.WriteLine(r);
});
t.RunSynchronously();
Console.ReadLine();
}
ですから、async / awaitの鍵となる結果が欲しいときにコントロールを解放して再同期するということです。
注:このコードの作成でスレッドはブロックされませんでした:)
私は時々混乱が何かがそれ自身のスレッドで実行されていることを意味しない「タスク」から来るかもしれないと思います。それはただすることを意味するだけで、非同期/待機はタスクをステージに分割し、それらのさまざまなステージをフローに調整することを可能にします。
それは料理のようなものです、あなたはレシピに従います。あなたは料理のために皿を組み立てる前にすべての準備作業をする必要があります。それでオーブンの電源を入れ、物を切ったり、物をかき回したりします。それから、オーブンの温度を待って準備作業を待ちます。論理的に思える方法でタスク間を自分で交換することでそれを行うことができます(tasks / async / await)が、物事を早くするためにニンジン(スレッド)を切り刻んでいる間、他の誰かにチーズのおろしを手伝ってもらうことができます。