Future や ExecutionContext をなんとなく触ってる人のために

Scala の Future 使おうとすると ExecutionContext とかいうの憑いてくるんだけどなんなんアイツ？

# ExecutionContext is 何？

ExecutionContext は Future に非同期処理の環境を提供する役割を担う。
Scala で非同期といえば Future というような解釈が間違っているわけでは無いが、
厳密には ExecutionContext が非同期実行を実現していると考えるほうが正しいと思う。

# scala.concurrent.ExecutionContext.Implicits.global について

これは、Scala が標準で提供している ExecutionContext である。
Future の実装を書いていて、下記エラーに遭遇したことが無い人はいないはず。

```scala
scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> def hoge = Future("test")
<console>:12: error: Cannot find an implicit ExecutionContext. You might pass
an (implicit ec: ExecutionContext) parameter to your method.

The ExecutionContext is used to configure how and on which
thread pools Futures will run, so the specific ExecutionContext
that is selected is important.

If your application does not define an ExecutionContext elsewhere,
consider using Scala's global ExecutionContext by defining
the following:

implicit val ec = ExecutionContext.global
       def hoge = Future("test")
```

これは Future.apply のシグネチャが下記のようになっていることを考えると理解が進む。

```scala
// Scala version is `2.12.8`
def apply[T](body: =>T)(implicit @deprecatedName('execctx) executor: ExecutionContext): Future[T]
```

つまり、第2引数の Implicit Parameter で ExecutionContext を要するために、
標準で実装があるので使ってくださいねと Scala さんがご丁寧に教えてくれているのだ。

# ExecutionContext.global を使う

アプリケーションの実行環境には、コア数という概念がある。
ExecutionContext.global を使うとコア数分のスレッドを消費して非同期処理を実行する。

なので、会社で提供していただいてるコア数8の私の MacBook Pro で実行すると下記のような挙動になる。

```scala
$ scala -version
Scala code runner version 2.12.8 -- Copyright 2002-2018, LAMP/EPFL and Lightbend, Inc.

$ scala

scala> :paste
// Entering paste mode (ctrl-D to finish)

import java.time.LocalDateTime
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def now(): LocalDateTime = LocalDateTime.now()

def doFuture(): Unit =
  (1 to 40).foreach(_ => Future{Thread.sleep(2000);println(now())})


// Exiting paste mode, now interpreting.

import java.time.LocalDateTime
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
now: ()java.time.LocalDateTime
doFuture: ()Unit

scala> doFuture()

scala> 2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747
2019-04-23T17:38:15.747 
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:17.748
2019-04-23T17:38:19.750
2019-04-23T17:38:19.750
2019-04-23T17:38:19.751
2019-04-23T17:38:19.750
2019-04-23T17:38:19.750
2019-04-23T17:38:19.750
2019-04-23T17:38:19.750
2019-04-23T17:38:19.751
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:21.756
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
2019-04-23T17:38:23.762
```

<strike>目を凝らして見てみると8件ずつ非同期処理がなされていることが見て取れる。</strike>

あとで強いひとに指摘されたんだけど MacBook Pro は8コアでは無いらしい。
HyperThreadingという技術で論理コアを増やしているだけで実際は4コアとのこと。

# 自分の体にフィットした ExecutionCotext を使おう

標準提供の ExecutionContext では要件が満たせないこともあるはずだ。
自分の体にフィットした ExecutionCotext を使おう。

 java.util.concurrent で作ったExecutor/ExecutorServiceから、
ExecutionContext.fromExecutor, ExecutionContext.fromExecutorService を使って作れば基本的には問題ないはず。

```scala
// Create ExecutionContext from java.util.concurrent.Executor
val myExecutors: ExecutorService = Executors.newFixedThreadPool(3)
val myExecutionContext1: ExecutionContextExecutor = ExecutionContext.fromExecutor(myExecutors)

// Create ExecutionContext from java.util.concurrent.ExecutorService
val myExecuteService: ForkJoinPool = new ForkJoinPool(3)
val myExecutionContext2: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(myExecuteService)
```

scala.concurrent.ExecutionContext.Implicits.global は ExecutionContextImpl.createDefaultExecutorService で生成した ExecutorService から作られている。参考にすると良いだろう。
https://github.com/scala/scala/blob/2.12.x/src/library/scala/concurrent/impl/ExecutionContextImpl.scala#L97

PlayFramework を利用しているのなら、独自に ExecutionContext を定義する仕組みが備わっているのでそちらを利用するのも良いだろう。
https://www.playframework.com/documentation/2.7.x/ThreadPools#Using-the-default-thread-pool

完全に理解した。

# その他

Scala 2.12.x 以降、 `scala.concurrent.ExecutionContext.Implicits.global`  の
プールされている最大スレッド数は256らしい。

https://github.com/scala/scala/pull/4090

なのでコア数8のマシンで  `scala.concurrent.ExecutionContext.Implicits.global` で非同期処理をした場合は、8件非同期実行して248スレッド余るみたいな感じになるのではと解釈した。

完全に理解した。

# 参考URL

https://github.com/scala/scala/pull/4090
https://docs.scala-lang.org/overviews/core/futures.html#the-global-execution-context
http://tototoshi.hatenablog.com/entry/2015/12/23/154104
https://mashi.hatenablog.com/entry/2014/11/24/010417
https://java.keicode.com/lang/multithreading-executor.php
https://www.playframework.com/documentation/2.7.x/ThreadPools#Using-the-default-thread-pool