Reputation: 180
I have the following code.
fun main(args: Array<String>) {
runBlocking {
while (true) {
launch {
println("Taking measurement")
val begin = System.currentTimeMillis()
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
println("Took measurement")
}
println("After launch")
delay(1000)
println("After delay")
}
}
}
I would expect the output of that to be twice as many "After launch"s and "After delay"s in the first 20 seconds. SO the output should look something like this:
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
... and then after 20s the first took measurements
But rather it looks like this and I can not wrap my head around to why it is not working.
After launch
Taking measurement
Took measurement
After delay
After launch
Taking measurement
What I basically want to achieve is a fire and forget. I want to start the code in launch and then 1 second after that I want to start it again.... The result of that code will be saved by that code, so I do not need any data back.
Any tips on why this is not working?
Upvotes: 0
Views: 2095
Reputation: 93834
This is because the default dispatcher of runBlocking
is a single-threaded dispatcher that uses the same thread that runBlocking
was called on. Since it is single-threaded, it cannot run the blocking portions of your coroutine concurrently. If you use runBlocking(Dispatchers.Default)
or launch(Dispatchers.Default)
to avoid running in the single-threaded dispatcher, it will behave as you had expected.
However, the only reason you ran into this surprise in the first place is that you are trying to do blocking work directly in a coroutine, which you're not supposed to do. You should not do blocking work in a coroutine without wrapping it in withContext
or a suspend function that uses withContext
. If you replace your blocking loop in the coroutine with a delay(1000)
suspend function call to simulate doing work in a non-blocking way, it will behave as you had expected.
When you do blocking work in a coroutine, you should either wrap it in a withContext
call like this:
withContext(Dispatchers.Default) {
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
}
or break it out into a suspend function that uses withContext
to perform the blocking work on an appropriate dispatcher:
suspend fun doLongCalculation() = withContext(Dispatchers.Default) {
val begin = System.currentTimeMillis()
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
}
This is why replacing your blocking work with a delay
suspend function call is an adequate simulation of doing long-running work. delay
and the doLongCalculation()
above are both proper suspend functions that do not block their caller or expect their caller to be using a specific dispatcher to function correctly.
Upvotes: 1