AsyncCallback

AsyncCallbackデリゲートに、非同期処理完了後に呼び出すメソッドを指定できる・・・。

AsyncCallback デリゲート (System)

まず、デリゲートの生成から・・・。

非同期処理で実行するasyncronousFunc、非同期処理完了後に呼び出すcallbackを生成・・・。

AsyncronousFuncに引数がなければ、asyncronousFunc.BeginInvokeの最初の引数にcallbackを指定する・・・。
(引数がある場合は、引数の分の後に指定・・・。)

AsyncronousFuncは"AsyncronousFunc(i)"の出力、AsyncCallbackFuncは"AsyncCallbackFunc!"の出力をする・・・。

asyncronousFunc.EndInvokeして、すぐ"Finish!"を出力して終わると、AsyncCallbackFuncを実行する暇もなく終わってしまう場合がある・・・。
なので、Thread.Sleepで3秒休止すると、AsyncCallbackFuncが実行されてから、Mainのその後の処理という流れになる・・・。

AsyncronousFunc(1)
AsyncronousFunc(2)
AsyncronousFunc(3)
AsyncronousFunc(4)
AsyncronousFunc(5)
AsyncCallbackFunc!
Finish!
続行するには何かキーを押してください . . .

こんな風に非同期処理AsyncronousFuncの後、AsyncCallbackFuncが実行されて終わる・・・。

Sample/dotnet/AsyncCallback/AsyncCallback/src/AsyncCallback_ at master · bg1bgst333/Sample · GitHub

IAsyncResult

IAsyncResultを使って、実行している非同期処理の状態を取得できる・・・。

IAsyncResult インターフェイス (System)

今回は、IAsyncResult.IsCompletedで非同期処理が完了しているかを1秒毎に確認する・・・。

今回は、引数も戻り値もないAsyncronousFuncDelegateに戻す・・・。
BeginInvokeで非同期処理を実行し、IAsyncResultインターフェイスとしてiarを取得・・・。

AsyncronousFuncは、

"AsyncronousFunc(i)"の出力に戻す・・・。

Main側では、

非同期処理の完了を表すiar.IsCompletedがfalseの間は、"AsyncronousFunc Running..."と出力して1秒休止を繰り返す・・・。
非同期処理が完了してtrueになればここを抜ける・・・。

すでに非同期処理は完了しているので、asyncronousFunc.EndInvokeはすぐ終わる・・・。

最後に"Finish!"と出力・・・。

AsyncronousFunc Running...
AsyncronousFunc(1)
AsyncronousFunc Running...
AsyncronousFunc(2)
AsyncronousFunc Running...
AsyncronousFunc(3)
AsyncronousFunc Running...
AsyncronousFunc(4)
AsyncronousFunc Running...
AsyncronousFunc(5)
AsyncronousFunc Running...
Finish!
続行するには何かキーを押してください . . .

このように、"AsyncronousFunc(5)"までいったら完了なので、"Finish!"で終わっている・・・。
("AsyncronousFunc(5)"のあと"AsyncronousFunc Running..."を出すだけの隙間時間はあるが・・・。)

Sample/dotnet/IAsyncResult/IAsyncResult/src/IAsyncResult_ at master · bg1bgst333/Sample · GitHub

EndInvoke

EndInvokeでは、非同期処理メソッドの戻り値を受け取ることができるし、参照渡しで受け取ることもできる・・・。

今回は、2つの値の足し算を非同期処理でさせて、結果を取得する・・・。

今回のAsyncronousFuncDelegateは、引数にint型のa, bとint型の参照渡しでrefValue、戻り値がintという感じに・・・。

渡す非同期処理メソッドAsyncronousFuncは、

aとbを足してresultに格納・・・。
5秒休止したあと、refValueにも結果を格納・・・。
戻り値としてresultを返す・・・。

Main側は、

BeginInvokeの最初の3つは、AsyncronousFuncに渡す引数・・・。
可変引数なので、後ろの2つ以外は引数の数だけ増える・・・。

"Calculating..."と出力・・・。

EndInvokeの最初も可変引数で、参照渡しの引数の数だけ増える・・・。
でrefValueを指定し、戻り値resultも返ってくる・・・。

resultとrefValueを出力・・・。

Calculating...
result = 30
refValue = 30
続行するには何かキーを押してください . . .

戻り値にも参照渡しにも計算結果が格納されている・・・。

Sample/cs/EndInvoke/EndInvoke/src/EndInvoke_ at master · bg1bgst333/Sample · GitHub

BeginInvoke

ThreadやThreadPoolを使わなくても、デリゲートで非同期処理が実行できる・・・。
(実際には内部でThreadPoolを使っているのだが・・・。)

Calling Synchronous Methods Asynchronously

delegate型を定義すると、C#コンパイラが自動でBeginInvoke、EndInvokeなどのメソッドを用意してくれて、デリゲートオブジェクトはそれらを使うことができる・・・。
(Delegateなどの基底クラスメソッドとかではないようなので、.NET FrameworkカテゴリではなくC#カテゴリにした・・・。)

引数も戻り値も無いデリゲートAsyncronousFuncDelegateを定義・・・。

AsyncronousFuncを非同期で実行するAsyncronousFuncDelegateオブジェクトasyncronousFuncを生成・・・。
AsyncronousFuncは、

AsyncronousFunc(i)の表示を1秒毎に計5回繰り返す・・・。

asyncronousFunc.BeginInvokeで非同期実行開始・・・。

こちらはMain(i)の表示を1秒毎に計3回繰り返す・・・。
なので、Mainの方が先に終わる・・・。

asyncronousFunc.EndInvokeで非同期処理が終わるまで待っている状態になる・・・。

asyncronousFunc.EndInvokeから抜けたら"Finish!"と表示・・・。

Main(1)
AsyncronousFunc(1)
Main(2)
AsyncronousFunc(2)
Main(3)
AsyncronousFunc(3)
AsyncronousFunc(4)
AsyncronousFunc(5)
Finish!
続行するには何かキーを押してください . . .

(3)までは、MainとAsyncronousFuncは1つずつ同時並行で出力される・・・。
しかし、AsyncronousFuncは(4)と(5)が残っているので、それらが出力し終わるまで、Mainは待ち続ける・・・。
そして終わったら、"Finish!"を表示・・・。

Sample/cs/BeginInvoke/BeginInvoke/src/BeginInvoke_ at master · bg1bgst333/Sample · GitHub

ThreadPool.QueueUserWorkItem

ThreadPool.QueueUserWorkItemの2番目の引数には、実行するThreadFuncに渡すためのパラメータを指定できる・・・。

ThreadPool.QueueUserWorkItem メソッド (WaitCallback, Object) (System.Threading)

ワークアイテムに番号を振る・・・。

こんな感じで、1~3をパラメータとして渡す・・・。

指定したパラメータはobject型stateとして渡されるので、intに戻してこれも"["と"]"で括って出力してあげる・・・。

Main(1)
ThreadFunc[1](1)
Main(2)
ThreadFunc[1](2)
ThreadFunc[2](1)
Main(3)
ThreadFunc[1](3)
ThreadFunc[2](2)
Main(4)
ThreadFunc[1](4)
ThreadFunc[2](3)
Main(5)
ThreadFunc[1](5)
ThreadFunc[3](1)
ThreadFunc[2](4)
ThreadFunc[3](2)
ThreadFunc[2](5)
ThreadFunc[3](3)
ThreadFunc[3](4)
ThreadFunc[3](5)
続行するには何かキーを押してください . . .

これでどのワークアイテムかわかりやすくなった・・・。

Sample/dotnet/ThreadPool/QueueUserWorkItem/src/ThreadPool_ at master · bg1bgst333/Sample · GitHub

ThreadPool

定量のスレッドを準備しておき、キューに登録したワークアイテム(実行したい処理(タスク))にそれぞれ割り当てて、終わった後も再び使いまわせるような仕組みをスレッドプールという・・・。

ThreadPoolは、そのスレッドプールを扱うクラス・・・。

ThreadPool クラス (System.Threading)

Mainでのループ処理に加えて、スレッドプールにThreadFuncのタスクを3つ割り当てる・・・。

まず、WaitCallbackデリゲート(これはSystem.Threading名前空間に用意されている既定もの)のwaitCallbackにThreadFuncを登録・・・。

ThreadFuncは、

前回までと似ているが、引数にobject型stateがある部分だけ違う・・・。
これは、WaitCallbackデリゲートの引数型がそうなっているから・・・。

WaitCallback デリゲート (System.Threading)

今回はstateは使わない・・・。

ThreadPool.QueueUserWorkItemでwaitCallbackを3回登録・・・。
これでスレッドプールで3つ並行してループ処理が走ることになる・・・。

さらに、メイン側もループを用意・・・。
なのでメインスレッドとスレッドプールを合わせると4つ並行・・・。

メインスレッドが先に終わってしまうと、スレッドプールの残りの出力が出ない場合があるので、何かキーが押されるまで待つ・・・。

実行すると、

Main(1)
ThreadFunc(1)
ThreadFunc(2)
Main(2)
ThreadFunc(1)
ThreadFunc(1)
ThreadFunc(3)
Main(3)
ThreadFunc(2)
ThreadFunc(2)
ThreadFunc(4)
Main(4)
ThreadFunc(3)
ThreadFunc(3)
ThreadFunc(5)
Main(5)
ThreadFunc(4)
ThreadFunc(4)
ThreadFunc(5)
ThreadFunc(5)
続行するには何かキーを押してください . . .

結果だけ見るとこうだが、実際に実行してみると、1秒ごとのタイミングじゃないタイミングで出力されたりする・・・。
そして、一気に進むやつと遅れるやつが出てくる・・・。

Sample/dotnet/ThreadPool/ThreadPool/src/ThreadPool_ at master · bg1bgst333/Sample · GitHub

Thread.Sleep

スレッドを一時的に止めるには、Thread.Sleepに休止する時間をミリ秒で指定する・・・。

Thread.Sleep メソッド (Int32) (System.Threading)

前回の"Main~"や"Thread~"を1つ出力するたびに1秒停止というのを繰り返す・・・。

こんな感じで、MainもThreadFuncも1秒ごとに出力するようにする・・・。

すると最初は、

ThreadFunc(1)
Main(1)

1秒後は、

ThreadFunc(1)
Main(1)
ThreadFunc(2)
Main(2)

という感じで、1秒ごとに同時に出力されてる・・・。
最終的に、

ThreadFunc(1)
Main(1)
ThreadFunc(2)
Main(2)
ThreadFunc(3)
Main(3)
ThreadFunc(4)
Main(4)
ThreadFunc(5)
Main(5)
続行するには何かキーを押してください . . .

となる・・・。
メインスレッドと別スレッド(ワーカースレッドともいう・・・。)が同時に処理してるのがわかる・・・。

また、場合によっては、

ThreadFunc(1)
Main(1)
Main(2)
ThreadFunc(2)
ThreadFunc(3)
Main(3)
Main(4)
ThreadFunc(4)
ThreadFunc(5)
Main(5)
続行するには何かキーを押してください . . .

こうなる場合もあり、どちらが先に出力されるかも不定・・・。

Sample/dotnet/Thread/Sleep/src/Thread_ at master · bg1bgst333/Sample · GitHub