参考:Scala公式ドキュメント Futureの項目 (en / jp)
Futureの実行環境として、Scalaではscala.concurrent.ExecutionContext.Implicits.global というものがあり、implicitなExecutionContextとして、デフォルトでよさげなものを用意してくれます。このExecutionContextの挙動を確かめるために、ちょっとしたコードを書きました。
基本は、第一引数で指定した数のFutureを生成して走らせるだけです。Future内では、実行タイミングとスレッド番号を表示します。
% scala FutureBehaviorChecker.scala 1
Processors: 8
Thread active: 3
Current Thread: 1
Mode: sleep without blocking{}.
Finished: 00 [ 27 -> 228] at 10
% scala FutureBehaviorChecker.scala 6
Processors: 8
Thread active: 8
Current Thread: 1
Mode: sleep without blocking{}.
Finished: 00 [ 28 -> 228] at 10
Finished: 05 [ 31 -> 231] at 14
Finished: 02 [ 31 -> 231] at 13
Finished: 03 [ 31 -> 231] at 16
Finished: 04 [ 31 -> 231] at 17
Finished: 01 [ 31 -> 231] at 12
ExecutionContext.Implicits.globalの挙動
スレッドプール内のスレッド数は、プロセッサ数と同じだけの上限を持つようです。% scala FutureBehaviorChecker.scala 10
Processors: 8
Thread active: 9
Current Thread: 1
Mode: sleep without blocking{}.
Finished: 00 [ 33 -> 234] at 10
Finished: 04 [ 37 -> 237] at 14
Finished: 03 [ 37 -> 237] at 13
Finished: 05 [ 37 -> 237] at 15
Finished: 02 [ 37 -> 237] at 12
Finished: 01 [ 36 -> 236] at 11
Finished: 06 [ 37 -> 237] at 16
Finished: 07 [ 37 -> 237] at 17
Finished: 08 [ 246 -> 446] at 10
Finished: 09 [ 246 -> 446] at 14
9個目以降は、全スレッドがブロックしているので詰まってますね。まあデフォルトなんてこんなものか。と自前のExecutorContext指定の方法を調べかかりましたが、そこに手を出す前にもっと簡単な方法がありました。
blockingの指定
本家のドキュメント内ではさらりと流されていますが、ブロッキングしてしまうような迷惑なFutureに対しては、明示的にブロッキング動作をするものだと指定できるようです。参考: multithreading - Asynchronous IO in Scala with futures - Stack Overflow
自作のテストコードでは、第二引数に何か与えると、Thread.sleepをblockingで囲んだコードが走ります。
% scala FutureBehaviorChecker.scala 10 1
Processors: 8
Thread active: 12
Current Thread: 1
Mode: sleep with blocking{}.
Finished: 04 [ 33 -> 234] at 15
Finished: 06 [ 33 -> 234] at 16
Finished: 07 [ 34 -> 234] at 17
Finished: 01 [ 33 -> 234] at 11
Finished: 09 [ 35 -> 235] at 19
Finished: 05 [ 33 -> 234] at 14
Finished: 00 [ 29 -> 234] at 20
Finished: 08 [ 35 -> 235] at 18
Finished: 02 [ 33 -> 235] at 12
Finished: 03 [ 33 -> 234] at 13
使用するスレッド数が増えてますね。長くなるのでわざわざここには貼りませんが、100個同時に走らせれば100スレッド生成されるようです。スレッドを立てるので使用するリソースは増えるものの、全体がブロックすることはなくなります。
この大量に生成されるスレッドは、Futureごとにスレッドをひとつ立てるのではなく、あくまでもスレッドプールに増えるものです。Future実行に使用したスレッドは一定時間ごとにじわじわとshutdownされていきます。Futureを使った後、定期的にスレッド数をprintしてみると、じわじわと減っていくのがわかります。
まとめ
- Futureが走るスレッド数は有限
- ブロックする操作 or 時間がかかる操作なら、とりあえずはblockingをつける
プロトタイプや遊びで組むぶんには、blockingに頼ってしまってもいいかな。
0 件のコメント:
コメントを投稿