参考: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 149個目以降は、全スレッドがブロックしているので詰まってますね。
まあデフォルトなんてこんなものか。と自前の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 件のコメント:
コメントを投稿