Reputation: 1286
I am trying to build a new control structure which create a thread for each of its argument, and run them in parallel. The code seems to be fine when I built two future manually for each input, because I see the fast thread finishes before the slow thread.
Here is output:
fast
slow
However, if I use List(a, b).map(f => Future {f})
then I always see fast thread is executed after slow is done. Here is the output:
slow
fast
Can someone explain this?
code pasted here:
import java.util.concurrent.Executors
import scala.concurrent.{ExecutionContext, Future}
object ExecInParallel extends App {
def run(a: => Unit, b: => Unit): Unit = {
val executorService = Executors.newFixedThreadPool(2)
implicit val executionContext =
ExecutionContext.fromExecutorService(executorService)
// af and bf are executed in parallel
val af = Future(a)
val bf = Future(b)
// however, the following code is not parallel
List(a, b).map(f => Future(f))
Thread.sleep(3000)
executorService.shutdown
}
run(
{
Thread.sleep(2000)
println("slow")
},
{
Thread.sleep(1000)
println("fast")
}
)
}
Upvotes: 0
Views: 69
Reputation: 1806
Your by-name a
and b
is executed (sequentially) in List(a, b)
, before the construction of Future
in map
. If you check the inferred type of List(a, b)
you'd see it's a List[Unit]
.
To achieve what you intented, you need a list of functions rather than list of results.
The following will work:
List(a _, b _).map(f => Future(f()))
Upvotes: 1
Reputation: 12783
That's because a
and b
are evaluated every time they are referenced in a non-by-name position and List(a, b)
arguments are not by-name. From https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name:
Call by name is an evaluation strategy where the arguments to a function are not evaluated before the function is called—rather,... then left to be evaluated whenever they appear in the function. If an argument is not used in the function body, the argument is never evaluated; if it is used several times, it is re-evaluated each time it appears.
Effectively that is equivalent to this code:
List({
Thread.sleep(2000)
println("slow")
},
{
Thread.sleep(1000)
println("fast")
}).map(f => Future(f))
Since the List constructor doesn't take by-name arguments, these values are evaluated before the list itself is even constructed
Upvotes: 1
Reputation: 8866
This happens because you first create list of two call-by name values:
List(a, b)...
and until a and b not fully computed the map operation is not executed. When List(a,b) is ready you wrap it in Futures:
List(a, b).map(f => Future(f))
Upvotes: 1