Reputation: 1299
Can you please explain me what is the difference between these two blocks of code. First time prints 421, but second prints 606. Why first one is parallel and second one is sequential?
fun main(args: Array<String>) = runBlocking {
var time = measureTimeMillis {
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()
println(int1 + int2)
}
println(time)
time = measureTimeMillis {
val one = async { one() }.await()
val two = async { two() }.await()
println(one + two)
}
print(time)
}
suspend fun one(): Int {
delay(200)
return 12
}
suspend fun two(): Int {
delay(400)
return 23
}
Upvotes: 35
Views: 69037
Reputation: 1352
The thumb-rules:
Check this for more details
Upvotes: 8
Reputation: 23252
In the first variant you get a Deferred<Int>
for both async-calls. As the documentation of Deferred
shows so nicely there are several states the deferred object might be in. From the outside that state is now either new
or active
but surely not completed yet. On your second variant however the first async-await needs a completed
state already otherwise you could not have any value there. However on your async{one()}.await()
the second async
is not known yet.
Note also that the return value of await()
is now Int
and not Deferred
anymore, so the coroutine must have been executed by then. Check also the documentation of await()
.
In other words:
val one = async { one() }
val two = async { two() }
Both one
and two
are now Deferred<Int>
. None has been called yet (or might have been called yet). As soon as you call one.await()
it may start already both one
and two
, just because it has the resources for it (even if you didn't use two.await()
anywhere in your code).
On the second variant however:
val one = async { one() }.await()
val two = async { two() }.await()
Even though it creates a coroutine for async {one()}
it must set a value to one
immediately, because you are calling await()
on it. The types of one
and two
are both Int
. So as soon as the first of those lines is hit, the async code needs to be executed immediately. By then nobody knows that another asynchronous call has to be executed as we wait for the value of the first. If the first wouldn't have an await
, the coroutine would again be executed in parallel, e.g.:
val one = async { one() }
val two = async { two() }.await()
will execute one()
and two()
in parallel.
So maybe this can be summarized to: only those coroutines can be executed in parallel on an await, that are known/spawned by then.
Upvotes: 9
Reputation: 200148
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()
What this does:
val one = async { one() }.await()
val two = async { two() }.await()
What this does:
There's no concurrency here, it's purely sequential code. In fact, for sequential execution you shouldn't even use async
. The proper idiom is
val one = withContext(Dispatchers.Default) { one() }
val two = withContext(Dispatchers.Default) { two() }
Upvotes: 61