- 追加された行はこの色です。
- 削除された行はこの色です。
[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]
「[[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。
-[[戻る>非同期処理]]
* 目次 [#l24f314e]
#contents
*概要 [#d9ab14bf]
awaitの意味は、スレッドを留めずにCallbackを待つということらしい。~
async/awaitはそういう動きをする、非同期プログラムを容易に記述できる。
**はじめに [#rb3ca86b]
-async/awaitの登場で、マルチスレッド処理として実装しなくても、~
非同期処理を同期型処理と、ほぼ変わらない記述で実装可能になった。
非同期処理を同期型処理と、ほぼ変わらない記述で容易に実装可能になった。
-非同期処理はスレッドやWindowsメッセージングキューでTask化される。~
-awaitの意味は、
--スレッドを止めずノンブロッキングで
--非同期から同期に復帰する
>という意味らしい。
-実際、async/awaitはそういう動きをする。~
--スレッドやWindowsメッセージングキューでTask化される。~
昔懐かしい、[[ノンプリエンプティブ・マルチタスク]](Win3.1)のようなイメージ。
-しかし、その実態はとても複雑、詳しくは[[コチラ>#nc344611]]を参照のこと。
--しかし、その実態はとても複雑(詳しくは[[コチラ>#nc344611]]を参照)。
---内部がどう動いているか詳しく把握しないとシステムは安定しない。
---中身をシッカリ理解して使用している人が少ないので注意が必要。
**Thread、ThreadPool [#c79c02e2]
従来の、マルチスレッド・プログラミング。
**用途 [#kb0ddc1e]
主に、非同期処理(≠並列処理)を簡単に(同期処理的に)実装するために導入された。
-スレッドの並列実行はOSが裏で無意識にしてくれていた。
--タイムスライスで細切れ/ラウンドロビンで論理的に並列実行。
--CPUのコア数に応じて、物理的に並列実行。
-UIスレッドからのサーバ呼出のハングアップを防止するために使用される。
-しかし、以下の処理は意識的に実装する必要があった。
--非同期処理をスレッド関数として分離して実装する。
--スレッド関数を作成したワーカースレッドに渡す。
--スレッド関数の結果をメインスレッドで待ち合わせる。
-または、内部的には、UIスレッドから、時間のかかるバックグラウンド処理~
(ネットワーク バインドまたは I/O バインドの処理)を分離する。
**APM、EAP [#x24ad95f]
-APM(Asynchronous Programming Model)~
[[Control.Invoke>http://www.atmarkit.co.jp/ait/articles/0506/17/news111.html]]
-EAP(Event-based Asynchronous Pattern)~
[[BackgroundWorker>http://www.atmarkit.co.jp/fdotnet/dotnettips/436bgworker/bgworker.html]]
-Webサーバのスレッド枯渇を防ぐ[[非同期Controller>ASP.NET MVCの用語#lc319892]]などにも応用されている。
Open棟梁の「[[非同期呼出フレームワーク>https://opentouryo.osscons.jp/index.php?%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%91%BC%E5%87%BA%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF]]」がこの方式で実装されている。
-並列実行処理を実装するための基盤ではない。
**TAP [#ae25c1af]
async/awaitは、TAP(Task-based Asynchronous Pattern)の方式で実装されている。
--[[Fire & Forget>#hc2dffee]]やTask.WhenAllで並列実行処理を実装できるが、~
await、Task.Wait()の"待ち合わせ"までがセットになっている仕組みなので、~
そもそも、並列実行処理を実装することに特化した基盤ではないことに注意する。~
(非同期タスクを作成して待つ処理を同期的に書ける仕組みと考えるべきか。)
***用途 [#kb0ddc1e]
-主に、非同期処理(≠並列処理)を実装するために導入された。
-UIスレッドからのサーバ呼出のハングアップを防止するために使用される。
-内部的には、UIスレッドから、時間のかかるバックグラウンド処理~
(ネットワーク バインドまたは I/O バインドの処理)を分離する。
-この仕組みは、Webサーバのスレッド枯渇を防ぐ[[非同期Controller>ASP.NET MVCの用語#lc319892]]などにも応用されている。
--並列実行処理を実装する場合は、Threadや、ThreadPoolを使用すればイイ。
***使い方 [#l81605a4]
**使い方 [#l81605a4]
async/awaitの登場で、非同期処理を同期型処理と、ほぼ変わらない記述で容易に実装可能になった。
-asyncで修飾したTaskを返す非同期メソッドから、awaitステートメントを付与して呼び出す。
-若しくは、Task.Run()で実行して、Task.Wait()で待ち合わせる。
***タスク分割方法 [#gb4b4431]
しかし、デバッグの時は非同期で実行されていることを意識する必要がある。~
(しかも、かなり特殊な。同期コンテキスト毎に動作も大きく異なる。)
**仕組み [#kaa7ba64]
-非同期化からのノンブロッキングの復帰には同期コンテキストが使用される。
-同期コンテキストにはWindowsメッセージングキュー、ThreadPoolなどがある。
-仕組みの詳細については[[コチラ>#nc344611]]を参照。
async/await]]は、TAP(Task-based Asynchronous Pattern)の方式で実装されている。
async/awaitは、
-メッセージ(番号)のような制約のある単純固定長値のキューではなく、~
実行コードの任意のコード断片そのものをキュー(同期コンテキスト)を使用して繋いでいる。
-.NET 3.5用のasync/await互換NuGetパッケージでは、~
.NET 4のTask互換クラスを、内部をBeginInvoke等で実装して、async/awaitを使えるようにしていた。
**余談 [#ef267401]
async/awaitの夫々の意味。
-async修飾子~
asyncは、呼び出し元と同じ同期コンテキストで実行されることを示す。
-await演算子~
asyncをawaitすると、スレッドを止めずに同期コンテキストで同期する。
この動作は想像し難いが、具体的には、
-同じ同期コンテキスト(Windowsメッセージングキュー)に
-コールバックを順番に並べる(Control.BeginInvoke()的な)。
というイメージ。
-・・・結局、APM、EAPと同じ技術(同期コンテキスト)を使っている。
-なお、同期コンテキストの種類によって、動きも異なる。
--同期コンテキストはキュー的なもので実装されている。~
例えば、前述の、
---Windowsメッセージングキュー、
---ThreadPool
---I/O 完了ポート
---, etc.
--なので、言葉尻だけで動作を想像し難く、~
[[利用の際は注意が必要になってくる>#c3aa7761]]。
*実装方法 [#je886ba3]
**タスク分割方法 [#gb4b4431]
await(Task.Run)を切れ目として、~
プログラマが意識して時間のかかるバックグラウンド処理~
(ネットワーク バインドまたは I/O バインドの処理)を分割する。
-await
--await前の処理はフォアグラウンドで実行される。
--awaitで呼び出す非同期メソッドはバックグラウンドで実行される。
--await後の処理は、コールバックとして実装せずにフォアグラウンドに復帰する。
***await [#d6f183d7]
-await前の処理はフォアグラウンド的に実行される。
-awaitで呼び出す非同期メソッドはバックグラウンド的に実行される。
-await後の処理は、コールバックとして実装せずにフォアグラウンドに復帰する。
--厳密に言うとフォアグラウンドに復帰ではなく、~
プログラムのコード上、非同期処理の後続処理に復帰する。~
--例えば、[[非同期Controller>ASP.NET MVCの用語#lc319892]]の非同期処理の後続処理は、
---非同期スレッド側で実行され、最後にリクエストを受け付けたスレッドにバインドされる。
---この動作は、システム的には、フォアグラウンドに復帰するとは言えない。
-Task.Run ~ Task.Wait()
--Task.Run前の処理はフォアグラウンドで実行される。
--Task.Run()で呼び出す非同期メソッドはバックグラウンドで実行される。
--Task.Wait()後の処理は、コールバックとして実装せずにフォアグラウンドに復帰する。
***Task.Run ~ Task.Wait() [#t097d303]
-Task.Run前の処理はフォアグラウンド的に実行される。
-Task.Run()で呼び出す非同期メソッドはバックグラウンド的に実行される。
-Task.Wait()後の処理は、コールバックとして実装せずして、フォアグラウンドに復帰する。
-Task.Waitが呼ばれるとスレッドはTaskが終わるまで待機する。
***仕組み [#kaa7ba64]
-非同期化は、同期コンテキスト(スレッドやWindowsメッセージングキュー)などを使用して行われる。
-await、Task.Wait()の待ち合わせまでがセットになっている仕組みなので、~
並列実行処理を実装するための基盤ではないことに注意。
-これらの動きの詳細は、同期コンテキストによって異なってくる。
-また、Task.Waitを使用すると、同期コンテキスト上、~
実行する非同期Taskの前に、Task.Waitが割り込むとデッドロックになったりする。
*使い方 [#nf166e6d]
-async/awaitの登場で、同期型処理と、ほぼ変わらない記述で実装可能になった。
-しかし、デバッグの時は非同期で実行されていることを意識する必要がある。
**非同期メソッドの戻り値 [#b43ff41c]
***void型 [#w5cf9ec2]
[[Fire & Forget>#hc2dffee]]の場合、戻り値は不要。
***Task型 [#t98c2e31]
[[Awaitable>#v7df61f1]]の場合、
-戻り値の無い非同期メソッドを実装する場合、Task型の戻り値が必要。
-基本的に、returnを書く必要はない。
-しかし、非同期メソッド内で非同期処理を呼び出さない場合、~
以下のように、完了状態のTaskを生成するreturnを書く必要がある。
[[Task.FromResult>#vfa22973]]で、完了状態のTaskを生成するreturnを書く必要がある。
await Task.FromResult(0);
await Task.FromResult(default(object));
***Task<T>型 [#td2f6464]
[[Awaitable>#v7df61f1]]の場合、
-T型の戻り値の有る非同期メソッドを実装する場合、Task<T>型の戻り値が必要。
-戻り値はTask<T>型だが、awaitした後、Tの型の値をreturnするように実装する。
int ret = await XXXXAsync();
return ret;
-しかし、非同期メソッド内で非同期処理を呼び出さない場合、~
以下のように、完了状態のTask<T>を生成するreturnを書く必要がある。
[[Task.FromResult>#vfa22973]]で、完了状態のTask<T>を生成するreturnを書く必要がある。
await Task.FromResult(new T());
***Task.FromResult [#vfa22973]
Task.FromResultを使用して、完了状態のTaskを生成することができる。
Task.FromResultを使用すると、完了状態のTaskを生成することができる。
詳細については、下記を参照。
-以下のようなケースで利用する。
--戻り値のTaskが長いコード パスを実行することなく、直ぐ完了する条件に合致する場合
--インターフェイス上はTaskを返すが、メソッド内で非同期処理を呼び出さない場合
-Task.FromResult(TResult) メソッド (TResult) (System.Threading.Tasks)~
-詳細については、下記を参照のこと。
--Task.FromResult(TResult) メソッド (TResult) (System.Threading.Tasks)~
https://msdn.microsoft.com/ja-jp/library/hh194922.aspx
--c# - If my interface must return Task what is the best way to have a no-operation implementation? - Stack Overflow~
https://stackoverflow.com/questions/13127177/if-my-interface-must-return-task-what-is-the-best-way-to-have-a-no-operation-imp
**非同期メソッドの呼び出し [#s278988c]
***Task.Run()、Task.Wait()メソッド [#l8b4142b]
-Task.Run()
--非同期メソッドを実行してTask、Task<TResult>を返す。
-Task.Wait()
--Task、Task<TResult>の実行が完了するまで待機する。
***await演算子 [#z7b2e8a4]
-Task の実行が完了したら、待機せずに、~
フォアグラウンドに復帰する風な動きを見せる。
***Task.WhenAll() [#j18f876f]
-Task.WhenAll()メソッドで複数のタスクを待機するタスクを取得する。
-このTaskをTask.Wait()するとTask毎の例外をAggregateException型として取得可能。
***await演算子 [#z7b2e8a4]
-Task の実行が完了するまで待機する。
--await 非同期メソッド()
--await Task.Run()
-await演算子の使い方
--非同期メソッドを呼び出すときに、await演算子(後述)を利用する。
--メソッド内でawait演算子(後述)を利用する場合、async修飾子でメソッドを修飾する。
--非同期メソッドを呼び出すときに、await演算子を利用する。
--メソッド内でawait演算子を利用する場合、async修飾子でメソッドを修飾する。
--await演算子は、async修飾子の付くメソッドの中で1つ以上記述できる。
***Task.Run()・Task.Wait()、Task.WhenAll()メソッド [#l8b4142b]
-Task.Run()メソッド
--非同期メソッドを実行してTask、Task<TResult>を返す。
-Task.Wait()
--Task、Task<TResult>の実行が完了するまで待機する。
--async/awaitと異なり、同期コンテキストで同期せず、~
待機(ブロック)した後にフォアグラウンドに復帰する。
-Task.WhenAll()
--メソッドで複数のタスクを待機するタスクを取得する。
--このTaskをTask.Wait()するとTask毎の~
例外をAggregateException型として取得可能。
**非同期メソッドの作り方 [#rc951c69]
***参考 [#uc071d5f]
- [.NET Framework 4/4.5][C# 5] 非同期で動くビジネスロジックを作る~
: biac の それさえもおそらくは幸せな日々@nifty~
http://bluewatersoft.cocolog-nifty.com/blog/2011/12/net-framework-4.html
-Task.Start()、Task.Factory.StartNew()、Task.Run()
--Task クラスと TaskFactory クラス~
http://www.kanazawa-net.ne.jp/~pmansato/parallel/parallel_taskfactory.htm
--Task.Start()とTask.Run()とTask.Factory.StartNew()の違い - From Full To Free~
http://fromfulltofree.blog.fc2.com/blog-entry-7.html
- [雑記] スレッド プールとタスク~
C# によるプログラミング入門 | ++C++; // 未確認飛行 C~
http://ufcpp.net/study/csharp/misc_task.html
-Provide an asynchronous version method for call the B-layer~
from the action method of asynchronous controller.~
· Issue #216 · OpenTouryoProject/OpenTouryo~
https://github.com/OpenTouryoProject/OpenTouryo/issues/216
*詳細 [#nc344611]
ここでは、
-「非同期メソッドの種類」と
-「同期コンテキスト」の
組み合わせによって、await後の処理が、~
どのように実行されるのかについて説明する。
-参考
--async/awaitと同時実行制御 | ++C++; // 未確認飛行 C ブログ~
https://ufcpp.wordpress.com/2012/11/12/asyncawait%E3%81%A8%E5%90%8C%E6%99%82%E5%AE%9F%E8%A1%8C%E5%88%B6%E5%BE%A1/
---An other world awaits you~
http://www.slideshare.net/ufcpp/an-other-world-awaits-you
**非同期メソッドの種類 [#yc35ca64]
非同期メソッドには、2種類ある。
-[[Fire & Forget>#hc2dffee]]~
戻り値がvoidのメソッド
-[[Awaitable>#v7df61f1]]~
戻り値がTask(もしくはTask<T>)のメソッド
***Fire & Forget [#hc2dffee]
-非同期呼び出しで投げっぱなす場合。~
呼び出し元が非同期メソッドの完了を待機する必要がない場合~
(この場合、非同期メソッドでもreturn文を書く必要が無い)。
非同期メソッドでもreturn文を書く必要が無い。
-具体的には、GUIアプリケーションのイベント・ハンドラに適用する場合。~
(それ以外の用途での利用はハマる原因になるらしいので非推奨らしい)
戻り値がvoidなので非同期以降がフォアグラウンドに復帰しない。
-非同期呼び出しで投げっぱなす場合。
-具体的には、GUIアプリケーションのイベント・ハンドラに適用する場合。
-上記以外の用途での利用は意味もなく、ハマる原因になるので非推奨。
***Awaitable [#v7df61f1]
-非同期呼び出しの呼び出し元でTask.Wait()で待ち合せする場合。~
呼び出し元が非同期メソッドの完了を待機する必要がある場合~
(この場合、非同期メソッドは、Task、Task<T>をリターンする)。
非同期メソッドは、Task、Task<T>をリターンする。
-上記のGUIアプリケーションのイベント・ハンドラ以外に適用する。
戻り値がTask(もしくはTask<T>)なので、await演算子かWait()メソッド~
以降に実装された非同期以降がフォアグラウンドに復帰する。
-非同期呼び出しの呼び出し元で
--awaitで、非同期以降をフォアグラウンドに復帰させる場合。
--Task.Wait()で呼び出し元が非同期メソッドの完了を待機する必要がある場合。
**同期コンテキスト [#je52c46b]
実行環境によって持つ同期コンテキストの種類が異なる。
***GUIアプリケーション [#yc5c5030]
GUIアプリケーションでの同期コンテキストは~
「Windowsメッセージングキュー(Control.Invoke)」になる。
「Windowsメッセージングキュー(Control.Invoke、.BeginInvokeで使う)」になる。
-Fire & Forget~
GUIアプリケーションのawaitの次の処理は、~
同期コンテキストのControl.Invokeによって、~
同期コンテキストのControl.BeginInvokeによって、~
UIスレッド上でシーケンシャルに実行される。
-Awaitable~
該当なし
該当なし?Control.Invokeでは?
***Consoleアプリケーション [#ibd79536]
Consoleアプリケーションでの同期コンテキストは「null」になる。
-Fire & Forget~
非推奨
非推奨(Threadや、ThreadPoolを使用すればイイ)
-Awaitable
--Consoleアプリケーション内のawaitの次の処理が実行されるスレッドは一意ではなくなる。
--ただし、処理自体は、シーケンシャルに実行される。
--ただし、処理自体は、(記述した順に)シーケンシャルに実行される。
***ThreadPool [#cf19c6b3]
Task.Runを使用した場合の同期コンテキストは、~
マルチスレッド環境下の「ThreadPool」になる。
-Fire & Forget~
非推奨
非推奨(Threadや、ThreadPoolを使用すればイイ)
-Awaitable
--Task.Run内のawaitの次の処理が実行されるスレッドは一意ではなくなり、
--且つ、Task.Run内のawaitの次の処理は、UIスレッドに戻らなくなる。
--ただし、処理自体は、シーケンシャルに実行される。
--ただし、処理自体は、(記述した順に)シーケンシャルに実行される。
***ASP.NET [#s4df3070]
ASP.NETアプリケーションでの同期コンテキストは~
マルチスレッド環境下の「System.Threading.SynchronizationContext.Current」になる。
-Fire & Forget~
非推奨
非推奨(Threadや、ThreadPoolを使用すればイイ)
-Awaitable
--ASP.NETアプリケーションのawaitの次の処理が実行されるスレッドは一意ではなくなる。
--ただし、処理自体は、シーケンシャルに実行される。
--ただし、処理自体は、(記述した順に)シーケンシャルに実行される。
--HttpContext等の保持、非同期処理が全て終わるまで、レスポンスしないよう監視
--最終的に結果は、リクエストを受け付けたスレッドにバインドされる。
詳しくは、[[非同期Controller>ASP.NET MVCの用語#lc319892]]を参考にする。
この辺の
-asp.net mvc - MVC Action Filters Collection was modified;~
enumeration operation may not execute - Stack Overflow~
http://stackoverflow.com/questions/29429526/mvc-action-filters-collection-was-modified-enumeration-operation-may-not-execut
スタック・トレースを見ると、
-「SynchronizationContext」と
-「AsyncControllerActioninvoker」は
同じ事らしいと解る。
なお、恐らく、これらの同期コンテキストは、~
I/O完了ポート(IOCP)= ノンブロッキングI/Oであると思われる。
-Can ASP.NET MVC's AsyncController be used to service large number of~
concurrent hanging requests (long poll)? - Stack Overflow~
http://stackoverflow.com/questions/4982882/can-asp-net-mvcs-asynccontroller-be-used-to-service-large-number-of-concurrent
***並列実行 [#k847f44a]
-Task.WhenAllで複数のTaskをTask.Wait()した場合(ThreadPoolで実行される)や、
-GUI以外の同期コンテキストでFire & Forgetを実行した場合(非推奨)は、
-GUI以外の同期コンテキストでFire & Forgetを実行した場合(非推奨)や、
-Task.WhenAllで複数のTaskをTask.Wait()した場合(ThreadPoolで実行される)では、
並列実行を始めるので、
特に、Consoleアプリケーションの同期コンテキスト下では、~
並列実行を始めるので、~
特に、Consoleアプリケーションの同期コンテキスト(null)の下では、~
新規に[[同期処理>#z135f913]]の実装が必要になることがある。
**ConfigureAwait [#vb891159]
awaitの挙動は、Awaitableパターンというものを満たすクラスを作ることで、自由にカスタマイズできるらしい。
*同期 [#w87411f4]
async/awaitは非同期呼び出しで投げっぱなした後に、~
同期コンテキストにより同期される方式のため、同期をあまり考慮していないが、~
同期コンテキストによっては、同期を行う必要があるため、以下に注意する。
*スレッド同期 [#w87411f4]
async/awaitは非同期呼び出しで投げっぱなす場合に利用されるため、~
スレッド同期をあまり考慮していないが、スレッド同期を行う場合は以下に注意する。
**仕組みから [#ab98fed9]
スレッドを使用した処理を記述しないが、
**仕組みから [#ab98fed9]
スレッドを使用した処理を記述しないが、分割されたタスクは、
分割されたタスクは、
-同一スレッドで動作することも
-別スレッドで動作することもある。
このため、一連の処理が(分割されたタスクが)、
-異なるスレッドで実装される保証は無い。
-同じスレッドで実行される保証も無い。
**スレッド同期ツールキットは使用不可 [#sdd9c0ad]
従って、スレッド同期のlock等は無意味。~
従来のスレッド同期ツールキットは使用不可。
***lock/mutex/semaphoreはtaskで全て使用禁止 [#offf1b54]
-旧プログラムでmutex使ってる場合に、awaitで追加開発したいときは、SemaphoreSlimに修正が必要
-.NET TIPS:非同期:awaitを含むコードをロックするには?(SemaphoreSlim編)[C#、VB] - @IT~
http://www.atmarkit.co.jp/ait/spv/1411/11/news117.html
--SemaphoreSlim.WaitAsync関数もasync/awaitする。
--つまり、Semaphoreによるlockさえ、タスクとして分割されて待機/実行される。
旧プログラムで、
+スレッド・アフィニティのあるロック機構(lock/mutex/semaphore)を使用してコードブロックをロックしている場合に、
+awaitを使用して修正変更をしたい場合(await演算子を含むコードブロックをロックしたい場合)、
スレッド・アフィニティのないロック機構を使用する。
***WaitFor[Single|Multi]Objectは例外的に使用可 [#z135f913]
これは、Win32の待機関数のWaitFor[Single|Multi]Objectは、~
スレッドに限らず様々な「オブジェクト」の状態が変化する(シグナル状態になる)のを待つ関数であるためと考える。
Win32の待機関数のWaitFor[Single|Multi]Objectは例外的に使用可。
-ThreadPool.RegisterWaitForSingleObject メソッド (System.Threading)~
これらの待機関数は、
-スレッド・アフィニティではなく、
-「状態変化」(ノンシグナル状態からシグナル状態への変化)を
待つ関数であるためと考える。
-スレッド・アフィニティのないロック機構
--.NET TIPS:非同期:awaitを含むコードをロックするには?(SemaphoreSlim編)[C#、VB] - @IT~
http://www.atmarkit.co.jp/ait/spv/1411/11/news117.html
---SemaphoreSlim.WaitAsync関数もasync/awaitする。
---Semaphoreによるlockさえ、タスクとして分割されて待機/実行される。
--ThreadPool クラス (System.Threading)~
https://msdn.microsoft.com/ja-jp/library/system.threading.threadpool.aspx
---ThreadPool.RegisterWaitForSingleObject メソッド (System.Threading)~
https://msdn.microsoft.com/ja-jp/library/system.threading.threadpool.registerwaitforsingleobject
--EventWaitHandle クラス (System.Threading)~
https://msdn.microsoft.com/ja-jp/library/system.threading.eventwaithandle.aspx
---AutoResetEvent クラス (System.Threading)~
https://msdn.microsoft.com/ja-jp/library/system.threading.autoresetevent.aspx
---ManualResetEvent クラス (System.Threading)~
https://msdn.microsoft.com/ja-jp/library/system.threading.manualresetevent.aspx
*その他 [#r19de18d]
**進捗報告 [#p80858da]
上記の仕組みで動いているとすると、進捗報告をどう実装するかが?であるが、~
以下を見ると、Progressクラス、IProgress<T>インターフェースを使用するらしい事が解る。
-非同期メソッド - C# によるプログラミング入門 | ++C++; // 未確認飛行 C~
http://ufcpp.net/study/csharp/sp5_async.html#cancel
-.NET TIPS:WPF/Windowsフォーム:~
時間のかかる処理をバックグラウンドで実行するには?(async/await編)[C#/VB] - @IT~
http://www.atmarkit.co.jp/ait/articles/1512/02/news019.html
詳しい仕組みは不明だが、GUI上で動作しているため、~
同期コンテキストのControl.Invokeを使用しているものと思われる。
同期コンテキストのControl.Invoke、.BeginInvokeを使用しているものと思われる。
*参考 [#l0e9a88c]
**CancellationToken [#we443ca7]
-.NET4.0 から CancellationToken を利用したタスクのキャンセル機構が導入された。
--タスク開始前にトークンがキャンセルされてるかのチェックするため、タスクを開始するコストが減る。
--ThrowIfCancellationRequestedメソッドでOperationCanceledException(OCE)をスローした場合、
---タスクの本体がOCEに含まれるキャンセルトークンも監視しており、
---同じだったら、タスクのステータスはCanceledになる(普通は、Faulted)。
-参考
--【C#】Taskをキャンセルする - vaguely~
http://mslgt.hatenablog.com/entry/2017/10/08/091841
--【C#】タスクのキャンセル方法 - Tumbling Dice~
http://outofmem.hatenablog.com/entry/2014/04/02/014201
--[[C# Taskの引数に使うCancellationTokenは何に使われているのか? - 株式会社リッカ>http://www.ricka.co.jp/2018/06/18/c-task%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%AB%E4%BD%BF%E3%81%86cancellationtoken%E3%81%AF%E4%BD%95%E3%81%AB%E4%BD%BF%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F/]]
---c# - Cancellation token in Task constructor: why? - Stack Overflow~
https://stackoverflow.com/questions/3712939/cancellation-token-in-task-constructor-why
--Microsoft Docs
---マネージド スレッドのキャンセル~
https://docs.microsoft.com/ja-jp/dotnet/standard/threading/cancellation-in-managed-threads
---CancellationToken Struct (System.Threading)~
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.cancellationtoken
--Qiita
---Cancellation Token について調べてみる~
https://qiita.com/TsuyoshiUshio@github/items/b2d23b37b410a2cfd330
---非同期で複数処理を実行し、対話式で制御する~
https://qiita.com/haminiku/items/cc299c1ed94d7ba3f9ec
**同期コンテキストの利用オプション [#k890ba74]
***ContinueWith [#vb891159]
-複数のタスクを継続に連結し、時間のかかる同期コンテキストを経由しない。
-参考
--方法: 複数のタスクを継続に連結する~
https://msdn.microsoft.com/ja-jp/library/dd537612.aspx
--Task.ContinueWith メソッド (System.Threading.Tasks)~
https://msdn.microsoft.com/ja-jp/library/system.threading.tasks.task.continuewith.aspx
***ConfigureAwait [#vb891159]
-同期コンテキストを使用するか・しないかを制御できる。
-参考
--Task.ConfigureAwait メソッド (Boolean) (System.Threading.Tasks)~
https://msdn.microsoft.com/ja-jp/library/system.threading.tasks.task.configureawait.aspx
*ガイドライン [#c3aa7761]
以下の参考資料から、ガイドラインを纏めてみた。
**一般的に [#l7670f2c]
***戻り値がvoidのメソッドを非同期呼び出ししない。 [#f9d2c463]
-呼び出し側でタスクの終了を検出することができない。
-タスクで発生した例外を呼び出し側で補足することができない。
-例外:イベントハンドラはOK(そもそもイベントハンドラ用)。~
→ 前項の理由のような挙動でも問題ないが無いため。
***Task.Wait() を使う場合は注意する [#t55295a3]
-Task.Wait()は、非同期処理を待機するため、デッドロックの原因になり易い。
-実行する非同期Taskの前に、Task.Waitが割り込むとデッドロックになったりする。
以下、デッドロックのサンプル。
-UIの場合のサンプル~
同期コンテキストであるWindowsメッセージングキューに、~
Task.Wait、非同期Taskの順でキューイングされるため~
前者が後者の完了を待ち続け、後者が何時迄も実行されないため(と思われる)。
--[C#] 同期メソッドから非同期メソッドを呼び出すと~
アプリケーションがフリーズする - 非同期メソッド呼び出しによるデッドロック~
https://www.ipentec.com/document/document.aspx?page=csharp-deadlock-call-async-method-from-sync-method
-ASP.NETの場合のサンプル~
同期コンテキストであるI/O完了ポートに、~
Task.Result(≒Task.Wait)、非同期Taskの順でキューイングされるため~
前者が後者の完了を待ち続け、後者が何時迄も実行されないため(と思われる)。
--ASP.NET で非同期 (Async) を乗りこなす – Tsmatz~
https://blogs.msdn.microsoft.com/tsmatsuz/2012/05/08/asp-net-mvc-async/
---await で Post された同期処理は、GetHeader().Result(非同期処理) の処理が終わるまで待機。
---同時に、GetHeader().Result(非同期処理) は、この Post された同期処理を待機。
-参考
--Task.Waitの問題点~
http://outside6.wp.xdomain.jp/2016/08/06/post-343/
--async/await ~非同期なライブラリは楽じゃない~ - 飽きっぽい人のブログ@qwerty2501~
http://qwerty2501.hatenablog.com/entry/2014/04/24/235849
**ライブラリの場合 [#g2ae71ea]
ざっくり、以下のガイドラインに従う。
-基本的に同期で実装する。
-非同期は、async/awaitを使用しない従来の非同期で実装する。
--コールスタックの下位でasyncを使うと、呼出側もasyncの使用が必要になる。
--そのため、一番外側までasyncを使うようにする必要がある。
--しかし、動作を変えることができないケースがあるので、~
Task.Waitやその他のブロック手段を使って同期を取るしかなくなる。
-async/awaitを使用する場合、
--Taskを返すだけにして、Task.Runは使わないようにする。
--ライブラリ内でawaitする場合は、ConfigureAwait(false)を使う。
詳しくは、下記を参照のこと。
***ライブラリ内でTask.Runを使わない [#d1bc9150]
-アンチパターン
public static async Task FetchFileAsync(int fileNum)
{
await Task.Run(() =>
{
var contents = IO.DownloadFile();
Console.WriteLine("Fetched file #{0}: {1}", fileNum, contents);
});
}
-理由:
--ライブラリがグローバル共有リソースであるThreadPoolを使用することになる。
--ライブラリは、実行コンテキストが不明なので、ThreadPool利用の決定は、~
ライブラリ開発者ではなくアプリケーション開発者がするべき。
--ライブラリが非同期メソッドを提供するのはネイティブ非同期メソッドを使用する場合。
---ネイティブ非同期メソッドはスレッドプールを使った別スレッドによる非同期処理を目的としていない。
---I/Oなどの待ちに対してスレッドを空けて同時実効性を高めることが目的。
-サーバでのTask.Run
--サーバでTask.Runを使わない。
--理由:
---Task.Runはスケーラビリティが求められるサーバでは不適切
---I/Oバウンドの場合(CPUバウンドでない場合)だけ、非同期tタスクを定義する意味があるが、~
この決定はライブラリ開発者ではなくアプリケーション開発者がするもの。
-クライアントでのTask.Run
--クライアントでも、Task.Runを使わない。
--理由:
---クライアント側ではTask.Runを使う理由がたくさんある。
---しかし前述にあるように、実行コンテキストが不明なので、ライブラリ内でTask.Runを使わない。
-例外
--例外: マルチスレッドとWinJS~
WinJSは新しいバックグラウンドスレッドを作ることができないので、かわりにライブラリ側で作る必要がある。
--例外: Stream.ReadAsync~
---ある種のストリームはこれをサポートしない。
---サポートされない場合は、基底クラス(Steamクラス)でTask.Runを実行するのが最も安全な方法
***Waitを使う同期メソッドで非同期メソッドをラップしない [#p43952f0]
-これは以下の様なユーザの仮定に基づくため。
--同期バージョンの方が非同期バージョンより高速(と言う仮定)。~
非同期バージョンより高速な同期バージョンを提供できない場合、
---両方のバージョンを提供する(=ラップを提供する)理由が無い。
---非同期バージョンを呼び出すときにTask.Waitを使って同期をとるほうが良い。
--同期バージョンは、UIスレッドで実行しても安全(と言う仮定)。~
非同期メソッドをラップした同期メソッドがTask.Waitを使っていた場合、デッドロックが発生する可能性がある。
-従って、推奨は、
--メソッドが同期処理を行うなら、同期バージョンだけを提供する。
--メソッドが非同期処理を行うなら、
---非同期バージョンだけを提供する。
---高速に動作するデッドロックを起こさない同期メソッドのみ追加で定義可能。
***デッドロックと同期コンテキスト [#i6758c6b]
-基本的にはライブラリ内でawaitする場合はConfigureAwait(false)する。
--性能的に早くなる。
--UIの場合の同期コンテキストであるWindowsメッセージングキューなど、~
同期コンテキストによっては、デッドロックさせる可能性が高くなる。
-ConfigureAwait(false)すると元のスレッド(主にUIスレッド)には戻らない。~
必要に応じて、同期コンテキストによる動作スレッドの切り替えを行う。
// UIスレッドの同期コンテキストをキャッシュする
SynchronizationContext syncContext = SynchronizationContext.Current;
//.ConfigureAwait(false)でUIスレッドに戻さない
await HeavyWorkAsync().ConfigureAwait(false);
// UIスレッドの同期コンテキストにディスパッチする。
syncContext.Post(state =>
{
// 何からの処理。
}, null);
***性能についての考察 [#y1e4a6ca]
-実行コンテキストをコピーする。
--ログイン・ユーザやカルチャ情報など[[偽装>サービス・タスク系のアカウント問題#vaded437]]をする場合、~
CallContext.SetLocalDataを使用して、実行コンテキストをコピーする。
--この処理は非同期呼び出しに少量の性能コストを追加する。
-ループ内で呼び出さない。
--asyncを使ったメソッドはTaskの生成やTaskの実行管理のためのコストがかかる。
--従って、ライブラリのユーザにはループ内で呼び出さないように注意喚起する。
-参考
--Async/Await- パフォーマンス上のオーバーヘッドと他の落とし穴~
http://www.infoq.com/jp/news/2013/07/async-await-pitfalls
---Async/Await - Best Practices in Asynchronous Programming~
http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
***メモリについての考察 [#j268c8a9]
非同期メソッドの呼び出しは次の3つのメモリ確保処理を生む。
-◯:ローカルの変数を保存するためのステートマシン
-◯:継続のためのデリゲート
-結果を返すためのタスク
◯が付与された、ステートマシンとデリゲートはawaitキーワードが~
ランタイムに現れたときに作成されるため、同期処理と比べるとコストになる。
**参考 [#w53a7cb8]
***落とし穴 [#r5c84421]
-C#の非同期の落とし穴~
https://www.infoq.com/jp/news/2013/04/async-csharp-fsharp
-Async in C# and F#: Asynchronous gotchas in C# (Japanese translation)~
https://gist.github.com/pocketberserker/5565303
-neue cc
--asyncの落とし穴Part2, SynchronizationContextの向こう側~
http://neue.cc/2013/07/02_412.html
--asyncの落とし穴Part3, async voidを避けるべき100億の理由~
http://neue.cc/2013/10/10_429.html
***ガイドライン [#x2b8b132]
-TAP (Task-based Asynchronous Pattern) 非同期メソッドのガイドライン - Qiita~
http://qiita.com/chocolamint/items/ed4999cccf011653cb78
-.NETで非同期ライブラリを正しく実装する~
https://www.infoq.com/jp/articles/Async-API-Design
--Creating Async Libraries That Are Modular, Reusable and Fast,~
in Microsoft Visual C# and Visual Basic | TechEd Europe 2013 | Channel 9~
https://channel9.msdn.com/Events/TechEd/Europe/2013/DEV-B318
-Tasks are (still) not threads and async is not parallel~
http://blogs.msdn.com/b/benwilli/archive/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel.aspx
-c# - Wrapping ManualResetEvent as awaitable task - Stack Overflow~
http://stackoverflow.com/questions/18756354/wrapping-manualresetevent-as-awaitable-task
-Insider.NET > 業務アプリInsider > 連載:C# 5.0&VB 11.0新機能「async/await非同期メソッド」入門 -@IT~
*参考 [#l0e9a88c]
-Insider.NET > 業務アプリInsider~
--連載:C# 5.0&VB 11.0新機能「async/await非同期メソッド」入門 -@IT~
http://www.atmarkit.co.jp/ait/subtop/features/dotnet/app/masterasync_index.html
-Taskを極めろ!async/await完全攻略 - Qiita~
http://qiita.com/acple@github/items/8f63aacb13de9954c5da
-ASP.NET で非同期 (Async) を乗りこなす | Tsmatz~
https://tsmatz.wordpress.com/2012/05/08/asp-net-mvc-async/
**xin9le.net [#aab910a7]
-TPL入門~
http://blog.xin9le.net/entry/tpl-intro
-非同期メソッド入門~
http://blog.xin9le.net/entry/async-method-intro
**MakCraft、徒然なブログ [#o74f57f1]
-非同期処理(インデックス) - MakCraft~
http://www.makcraft.com/devref/26-asynchronous-processing-index.html
***非同期処理 [#def5ad7d]
-(その1)~
http://www.makcraft.com/blog/meditation/2013/03/22/asynchronous-processing-part-1/
-(その2)~
http://www.makcraft.com/blog/meditation/2013/03/23/asynchronous-processing-part-2/
-(その3)~
http://www.makcraft.com/blog/meditation/2013/03/23/asynchronous-processing-part-3/
-(その4)~
http://www.makcraft.com/blog/meditation/2013/03/24/asynchronous-processing-part-4/
-(その5)~
http://www.makcraft.com/blog/meditation/2013/04/02/asynchronous-processing-part-5/
***プロデューサー/コンシューマー パターン [#j1d76f27]
-プロデューサー/コンシューマー パターン~
http://www.makcraft.com/blog/meditation/2013/03/24/producer-consumer-pattern/
-を利用したサーバー側接続処理~
http://www.makcraft.com/blog/meditation/2013/04/04/server-side-connection-processing-using-the-producer-consumer-pattern/
-を利用した IPv4 及び IPv6 接続待ち~
http://www.makcraft.com/blog/meditation/2013/04/06/listen-on-ipv6-and-ipv4-based-producer-consumer-pattern/
----
Tags: [[:プログラミング]], [[:.NET開発]]