[解決済み】async-awaitが追加のスレッドを作成しないのであれば、どのようにしてアプリケーションの応答性を高めているのでしょうか?
質問
を使用することは、何度も何度も言われています。
async
-
await
は、追加のスレッドを作成しません。コンピュータが一度に1つ以上のことを行っているように見える唯一の方法は、次のとおりです。
- 実際に1つ以上のことを同時に行っている(複数のプロセッサを利用した並列実行を行っている)。
- タスクのスケジューリングとその切り替えによるシミュレーション(Aを少し、Bを少し、Aを少し、など)
では、もし
async
-
await
はそのどちらも行わないのであれば、どうやってアプリケーションを応答性の高いものにできるでしょうか?もしスレッドが1つしかないのであれば、どんなメソッドを呼んでも、何かする前にそのメソッドの完了を待つことになり、そのメソッドの中のメソッドは結果を待ってから処理しなければならない、というように。
解決方法は?
実は、async/awaitはそれほど魔法のようなものではないのです。このトピックはかなり広範囲にわたりますが、あなたの質問に対する迅速かつ十分な回答は、私たちが管理できると思います。
Windows Forms アプリケーションの簡単なボタンクリックイベントに取り組んでみましょう。
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
私は
明示的に
ではなく
何であれ語る
GetSomethingAsync
がとりあえず返ってくる。これは、例えば2秒後に完了するものだと言っておきましょう。
従来の非同期の世界では、ボタンのクリックイベントハンドラーは次のようになります。
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
フォームのボタンをクリックすると、このメソッドが完了するのを待つ間、アプリケーションが2秒程度フリーズしているように見えます。これは、メッセージポンプがブロックされているためです。
このループは、ウィンドウズに対して継続的に "マウスを動かしたり、何かをクリックするなど、誰かが何かをしたのか?何かを塗り直す必要がありますか?もしそうなら、教えてください!" と、その "something" を処理しています。このループは、ユーザーが "button1" をクリックしたというメッセージ (または Windows からの同等のタイプのメッセージ) を受け取り、最終的に私たちの
button1_Click
というメソッドがあります。このメソッドが返ってくるまで、このループは待ちぼうけを食らうことになります。このループには2秒かかり、その間は何のメッセージも処理されません。
ウィンドウズを扱うほとんどのことはメッセージを使って行われます。つまり、メッセージループがほんの一瞬でもメッセージを送るのを止めると、ユーザーにはすぐに気づかれてしまうのです。例えば、メモ帳や他のプログラムを自分のプログラムの上に移動させ、また離れたら、今度は突然ウィンドウのどの領域が再び見えるようになったかを示すペイントメッセージが次々と自分のプログラムに送られるのです。これらのメッセージを処理するメッセージループが何かを待っていたり、ブロックされていたりすると、ペイントは行われません。
そこで、最初の例で言えば
async/await
は新しいスレッドを作成しませんが、どのようにそれを行うのでしょうか?
さて、何が起こるかというと、あなたのメソッドが2つに分かれるのです。これは大まかなトピックの1つなので、あまり詳しくは説明しませんが、メソッドがこの2つに分割されていることだけは確かです。
-
に至るまでのすべてのコードは
await
への呼び出しを含むGetSomethingAsync
-
に続くすべてのコード
await
イラストはこちら
code... code... code... await X(); ... code... code... code...
再整理した。
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
基本的にはこのようなメソッドが実行されます。
-
までを実行します。
await
-
を呼び出しています。
GetSomethingAsync
メソッドを実行し、その結果を返します。 2秒後の未来に完成するものここまでは、まだbutton1_Clickの最初の呼び出しの内部で、メッセージループから呼び出されたメインスレッドで起こっています。もし
await
に時間がかかると、やはりUIがフリーズしてしまいます。この例では、それほどでもありません。 -
何が
await
キーワードは、巧妙なコンパイラ・マジックと一緒に、基本的に次のようなことをします:OK, あのね、私はここで単にボタンのクリック・イベント・ハンドラから戻るつもりです。あなた(つまり、私たちが待っているもの)が完了する頃になったら、私に知らせてください、なぜならまだ実行すべきコードが残っているからです".実際には SynchronizationContext クラス が完了したことを知らせると、今実行している実際の同期コンテキストに応じて、実行のためのキューが作成されます。Windows Forms プログラムで使用されるコンテキストクラスは、メッセージループが汲み上げているキューを使用してキューイングします。
-
そのため、メッセージループに戻り、ウィンドウの移動、サイズ変更、他のボタンのクリックなど、自由にメッセージを送り続けることができるようになりました。
ユーザーにとっては、UI が再びレスポンシブになり、他のボタンのクリックやサイズ変更、そして最も重要なことを処理できるようになりました。 再描画 そのため、フリーズすることはないようです。
- 2秒後、私たちが待っているものが完了し、今起こっていることは、それ(まあ、同期コンテキスト)がメッセージループが見ているキューにメッセージを入れ、"ヘイ、あなたが実行するためのいくつかのコードを得た"と言い、このコードがすべてのコードであることです。 後 を実行します。
-
メッセージループがそのメッセージに到達すると、基本的にそのメソッドに再入力します。
await
で、そのメソッドの残りを実行し続ける。このコードは再びメッセージループから呼び出されるため、もしこのコードがasync/await
が適切に動作しないと、再びメッセージループがブロックされます。
ここでは、ボンネットの下に多くの可動部品があるため、より多くの情報へのリンクをいくつか紹介します。 は を知ることは、かなり重要です。 それらの可動部分の一部 . async/awaitがまだリーキーコンセプトであることは必ず理解できるはずです。そして、もしそうでなければ、通常、何の理由もなく不規則に壊れるアプリケーションをデバッグしなければならない羽目になります。
- AsyncとAwaitによる非同期プログラミング(C#とVisual Basic)
- SynchronizationContextクラス
- Stephen Cleary - There is no thread 一読の価値あり
- チャンネル9 - Mads Torgersen: Inside C# Async ぜひご覧ください。
OK、では、次の場合はどうでしょう。
GetSomethingAsync
は2秒後に完了するスレッドを紡ぎ出しますか?はい、その場合、明らかに新しいスレッドが存在しています。しかし、このスレッドは
なぜなら
このメソッドのプログラマが非同期コードを実装するためにスレッドを選択したからです。ほとんどすべての非同期I/O
はしない。
はスレッドを使用し、別のものを使用します。
async/await
自分自身で
は新しいスレッドを立ち上げませんが、明らかにquot; things we wait for"はスレッドを使用して実装されるかもしれません。
.NETには、必ずしも単独でスレッドをスピンアップしないものの、非同期であるものが多くあります。
- Webリクエスト(その他ネットワーク関連で時間がかかるものが多い)
- 非同期ファイル読み出し/書き込み
-
という名前のメソッドを持つクラス/インターフェースは、良いサインです。
SomethingSomethingAsync
またはBeginSomething
とEndSomething
があり、そこにIAsyncResult
を巻き込んだ。
通常、このようなものは、下手にスレッドを使用しない。
OK、では、そのquot;広い話題のもの"が欲しいのですね?
では、お願いします。 Roslynを試す ボタンクリックについて
生成されたクラスの全容はここではリンクしませんが、かなりグロい内容です。
関連
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み】Unity3DでOnTriggerEnterが動作しない件
-
[解決済み】「...は'型'であり、与えられたコンテキストでは有効ではありません」を解決するにはどうすればよいですか?(C#)
-
[解決済み】OnCollisionEnter2Dが実行されない?
-
[解決済み】Linq 構文 - 複数列の選択
-
VSでscanfエラーを恒久的に解決するには、ソースファイルを作成し、自動的に#define _CRT_SECURE_NO_WARNINGS 1を追加してください。
-
[解決済み】データが存在しないのに読み込もうとする試みが無効である
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] C#でawaitを使わずに非同期メソッドを安全に呼び出す方法
-
[解決済み】非同期プログラミングとマルチスレッドの違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C# [解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C#.
-
[解決済み】C#はJavaのcharAt()と同等?)
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】プロジェクトビルド時のエラー。エディタでスクリプトにコンパイルエラーがあるため、Playerのビルドにエラーが発生する
-
[解決済み】Sequence contains no matching element(シーケンスにマッチする要素がない
-
[解決済み】HRESULTからの例外:0x800A03ECエラー
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み】 C# 条件演算子エラー 代入、call、increment、decrement、await、new object 式のみ文として使用可能です。
-
[解決済み】別のスレッドがこのオブジェクトを所有しているため、呼び出し側のスレッドはこのオブジェクトにアクセスできない
-
[解決済み] ファイルディスクリプタとは何か、わかりやすく解説します。