I'm learning the Coroutines of kotlin.
The Image A can get the correct result.
I think I have used the code runBlocking, and main function will keep runing until it get final result, but Image B failed, why?
Image A
I'm learning the Coroutines of kotlin.
The Image A can get the correct result.
I think I have used the code runBlocking, and main function will keep runing until it get final result, but Image B failed, why?
Image A
When you use runBlocking, your code between the { } will run inside a CoroutineScope. If you run a child coroutine inside with launch, it will behave as you expected, because the parent coroutine has to wait for all its children before completing:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello, ")
}
However, when you use GlobalScope.launch to launch a new coroutine, it will not be tied to any parent, and therefore it won't be waited for, unless you add job.join().
See this:
Children of a coroutine
When a coroutine is launched in the CoroutineScope of another coroutine, it inherits its context via CoroutineScope.coroutineContext and the Job of the new coroutine becomes a child of the parent coroutine's job. When the parent coroutine is cancelled, all its children are recursively cancelled, too.
However, when GlobalScope is used to launch a coroutine, there is no parent for the job of the new coroutine. It is therefore not tied to the scope it was launched from and operates independently.
runBlocking will not return until all coroutines in its own scope have finished. Since your job is running on the GlobalScope instead it does not wait until it is finished. job.join() forces it to wait until the job (even from another scope) is done.
If you remove GlobalScope. it will launch on its internal scope and will work as you expect it should.
From the kotlin documentation about coroutines basics:
Structured concurrency
There is still something to be desired for practical usage of coroutines. When we use GlobalScope.launch, we create a top-level coroutine. Even though it is light-weight, it still consumes some memory resources while it runs. If we forget to keep a reference to the newly launched coroutine it still runs. What if the code in the coroutine hangs (for example, we erroneously delay for too long), what if we launched too many coroutines and ran out of memory? Having to manually keep references to all the launched coroutines and join them is error-prone.
There is a better solution. We can use structured concurrency in our code. Instead of launching coroutines in the GlobalScope, just like we usually do with threads (threads are always global), we can launch coroutines in the specific scope of the operation we are performing.
In our example, we have main function that is turned into a coroutine using runBlocking coroutine builder. Every coroutine builder, including runBlocking, adds an instance of CoroutineScope to the scope of its code block. We can launch coroutines in this scope without having to join them explicitly, because an outer coroutine (runBlocking in our example) does not complete until all the coroutines launched in its scope complete. Thus, we can make our example simpler:
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine in the scope of runBlocking
delay(1000L)
println("World!")
}
println("Hello,")
}
Adding to the point made by @huytc
To better understand the parent-child hierarchy you can use the job.children.contains like below
val customScope = CoroutineScope(Dispatchers.Default)
fun main() = runBlocking {
val job = customScope.launch {
println("processing")
delay(1000)
println("done processing")
}
println("Is custom scope child of run-blocking: ${this.coroutineContext.job.children.contains(job)}")
Output:
processing
Is custom scope child of run-blocking: false