The docs on Your first coroutine say -
The name of
runBlocking
means that the thread that runs it (in this case — the main thread) gets blocked for the duration of the call, until all the coroutines insiderunBlocking { ... }
complete their execution
Is it correct that the thread running runBlocking blocks?
If the main thread is blocked, then how the coroutine launched by launch { } in the below code (from the same doc) runs on the main thread??
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println(Thread.currentThread().name + " World!") // print after delay
}
println(Thread.currentThread().name + " Hello") // main coroutine continues while a previous one is delayed
}
My understanding is that
Here is an output from the above code -
main @coroutine#1 Hello
main @coroutine#2 World!
Same issue in the documentation of runBlocking
Runs a new coroutine and blocks the current thread until its completion. This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.
Compare the documentation you posted for runBlocking
Runs a new coroutine and blocks the current thread until its completion.
and launch
Launches a new coroutine without blocking the current thread and returns a reference to the coroutine as a Job.
They both create a new coroutine, it's just that launch
runs its coroutine asynchronously, and allows the caller to continue running code. runBlocking
doesn't allow the caller to continue until everything running in that coroutine has finished. So runBlocking
doesn't control the behaviour of the stuff inside its coroutine, just the execution of the code that's calling runBlocking
.
This tweak to your code might make things clearer:
fun main() {
runBlocking {
launch {
delay(1000L)
println(Thread.currentThread().name + " World!")
}
println(Thread.currentThread().name + " Hello")
}
// this -follows- the runBlocking statement
println("Hey I'm runnin' here!")
}
main @coroutine#1 Hello
main @coroutine#2 World!
Hey I'm runnin' here!
So what's happening is:
runBlocking
runs, blocking the main thread. The next line won't run until everything in this coroutine has finished!launch
runs, creating a new coroutine on the same thread, but without blocking it. The code on the next line will be able to run while this is running, because they're in separate coroutines, but taking turns in little time slices (since they're on the same thread). This coroutine calls delay
immediately, so even if it runs before the line in the original coroutine (the println
after launch)
it'll definitely hit its own println
much laterprintln
following launch
runs, since this coroutine is independent of the other one. This coroutine is finished (but the other one is still running)println
inside launch
runs after its delay. This coroutine has nothing else to do, so it's also finishedJob
s in the coroutine launched by runBlocking
have finished, that blocking coroutine ends, and execution can continue in the scope where it was launchedprintln
gets to run, finally!Also just to clear up a possible misunderstanding:
Is it correct that the thread running runBlocking blocks?
If the main thread is blocked, then how the coroutine launched by launch { } in the >below code (from the same doc) runs on the main thread??
All coroutines run on the same thread as the caller, unless you explicitly tell them not to, or use a dispatcher that can move them to a different thread. It's just that the coroutines "take turns" and get a bit of time to execute some code on a thread. So it looks like they're running "at the same time" even when they're not.
So because launch
doesn't "block", what that means is the coroutine that calls it is allowed to continue, getting a bit of time to execute the rest of the code, while the launched coroutine also gets some time slots. They can run independently.
But runBlocking
does block, which means it doesn't get any execution time until the coroutine it started finishes. It takes a break until the coroutine is done. So it's not actually blocking the main thread in the normal sense, otherwise none of the coroutines on that thread could run! It's just blocking execution in its own scope - it's a way to say "nothing else happens here until this job has finished". That's why it's a common main
function wrapper, to kind of create a world of coroutines that everything runs in, and preventing the main
function from completing and ending the program. But it does block anything outside that from running, which is why it's a bad idea in something like Android, where there's other stuff going on with the main thread besides any coroutines you might want to run.
Also, launch
actually runs on a CoroutineScope
(i.e. CoroutineScope.launch
) , which it has access to inside a coroutine - that's why you have to use it inside one! runBlocking
is a standalone builder function, but the function block you pass in has a type of CoroutineScope.() -> T
- i.e. it's going to be passed to a CoroutineScope
, like a default one provided by the framework. That CoroutineScope
is what works out when all its child coroutines have finished, and informs the caller that it's done. runBlocking
just waits around for that to happen, instead of allowing execution to continue like the coroutineScope
builder.