Вот определение метода, который неявно использует ExecutionContext
и позволяет клиенту переопределить его. Для проверки используются два контекста выполнения:
val defaultEc = ExecutionContext.fromExecutor(
Executors.newFixedThreadPool(5))
Имена потоков выглядят как: 'pool-1-thread-1' to 'pool-1-thread-5'
И второй от Scala:
scala.concurrent.ExecutionContext.Implicits.global
Имена потоков выглядят так: 'scala-execute-context-global-11'
Клиент может переопределить неявное значение по умолчанию через:
implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
К сожалению, он может быть переопределен только тогда, когда метод с неявным вызовом вызывается без ()
:
val r = FutureClient.f("testDefault") //prints scala-execution-context-global-11
не работает:
val r = FutureClient.f("testDefault")() //still prints: pool-1-thread-1
Вопрос в том, ПОЧЕМУ это работает так? Потому что это делает его намного сложнее для клиентов API
Вот полный код для запуска и воспроизведения:
object FutureClient {
//thread names will be from 'pool-1-thread-1' to 'pool-1-thread-5'
val defaultEc = ExecutionContext.fromExecutor(
Executors.newFixedThreadPool(5))
def f(beans: String)
(implicit executor:ExecutionContext = defaultEc)
: Future[String] = Future {
println("thread: " + Thread.currentThread().getName)
TimeUnit.SECONDS.sleep(Random.nextInt(3))
s"$beans"
}
}
class FutureTest {
//prints thread: pool-1-thread-1
@Test def testFDefault(): Unit ={
val r = FutureClient.f("testDefault")
while (!r.isCompleted) {
TimeUnit.SECONDS.sleep(2)
}
}
//thread: scala-execution-context-global-11
@Test def testFOverridable(): Unit ={
implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
val r = FutureClient.f("testDefault")
while (!r.isCompleted) {
TimeUnit.SECONDS.sleep(2)
}
}
//prints pool-1-thread-1, but not 'scala-execution-context-global-11'
//cause the client invokes f with () at the end
@Test def testFOverridableWrong(): Unit ={
implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
val r = FutureClient.f("testDefault")()
while (!r.isCompleted) {
TimeUnit.SECONDS.sleep(2)
}
}
}
Я уже обсуждал несколько связанных тем, но они связаны с определением API, поэтому это новая проблема, не охватываемая этими темами.