How to understand Coroutine Process with Example in kotlin - kotlin

I'm studying coroutine with docs and some examples.
testing some examples, I found that in same CoroutineScope it works sequentially.
For example
Below code is working sequentially.
fun main() = runBlocking {
coroutineScope {
launch {
delay(1000)
println("Start")
}
}
coroutineScope {
launch {
delay(2000)
println("World2")
}
launch {
delay(1000)
println("World1")
}
println("Hello")
}
println("Done")
}
/*
Start
Hello
World1
World2
Done
*/
But If I change above codes like this
fun main() = runBlocking {
launch {
delay(1000)
println("Start")
}
coroutineScope {
launch {
delay(2000)
println("World2")
}
launch {
delay(1000)
println("World1")
}
println("Hello")
}
println("Done")
}
/*
Hello
Start
World1
World2
Done
*/
Removing first CoroutineScope, the result sequence is changed.
In My Opinion, I think this is a problem of job state, but not sure.
Is there anyone who can clarify this issue?

coroutineScope suspends until all its children coroutines have finished their execution. In your first case, the coroutineScope waits until the launch completes and prints Start, that's why you see Start as the first output on console.
In the second part, you removed the coroutineScope, so now it just launches a new coroutine and moves to the next step (launch doesn't suspend). Then in the coroutineScope you launch two other coroutines (using launch) and print Hello. That's why the Hello gets printed first. All other print statements are waiting for the delay to finish.
After 1 second, the first launch completes and prints Start followed by World1 and World2. Now when this coroutineScope finishes, the program control moves to the final print statement and prints Done.

Related

Kotlin not waiting for a coroutine to finish?

I'm learning coroutines and am a little confused by something
import kotlinx.coroutines.*
#OptIn(DelicateCoroutinesApi::class)
fun main() {
println("[${Thread.currentThread().name}] In main thread")
GlobalScope.launch {
println("[${Thread.currentThread().name}] In coroutine")
repeat(4) {
println("$it")
}
}
}
When I run this code, the output is a little unpredictable, sometimes I get output from the couroutines, sometimes I don't..
could someone please explain what is causing this? Thank you!
I believe there are a few options, but the heart of the matter is the main thread believes your program is done, so it ends the program. You need to wait for the coroutine to complete. Try the below block. It waits for the coroutine to complete before proceeding:
val job = GlobalScope.launch {
println("[${Thread.currentThread().name}] In coroutine")
repeat(4) {
println("$it")
}
}
job.join()
more info can be found here, https://kotlinlang.org/docs/coroutines-basics.html#an-explicit-job

Kotlin async function not run in parallel

I have a kotlin function just as following, And I expected it can wrap a sync IO action into async.
suspend fun <T> runIOAsync(f:suspend () -> T): Deferred<T> = coroutineScope{
async(Dispatchers.IO) {
f()
}
}
Then I have my code in the calling side like
runBlocking {
repeat(5) {
runIOAsync {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}.await()
}
}
But the actual out put is
0
Thread[DefaultDispatcher-worker-1 #coroutine#2,5,main]
After sleep 0
1
Thread[DefaultDispatcher-worker-1 #coroutine#3,5,main]
After sleep 1
2
Thread[DefaultDispatcher-worker-1 #coroutine#4,5,main]
After sleep 2
3
Thread[DefaultDispatcher-worker-1 #coroutine#5,5,main]
After sleep 3
4
Thread[DefaultDispatcher-worker-1 #coroutine#6,5,main]
After sleep 4
Which seems all tasks from my function are executed serially. Any one can please help to give an explanation
Let's put aside runIOAsync for the moment.
You're using await() right after calling async(), which means that you're effectively waiting for the end of your async execution before executing the next one.
Instead, start all tasks first and then await all of them. For instance you can use awaitAll:
runBlocking {
List(5) {
async(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}.awaitAll()
}
Also, the way you're encapsulating the scope in runIOAsync is wrong, you will be waiting for the end of the async execution even without calling await() (coroutineScope is a suspending function that waits for all its child coroutines before resuming).
Instead, use coroutineScope to define the boundary of your coroutines executions, and you don't even have to await them. Since you don't need to get values from this code, you can also use launch instead of async here:
coroutineScope {
repeat(5) {
launch(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}
}
Declaring a suspend function returning a Deferred should be a red flag, and is quite confusing from an API standpoint: if you suspend it means you wait, if you return Deferred it means you don't wait (you immediately return a handle to some running computation). A function that does both would be quite weird.
If what you want is to make suspending code from IO-bound code, you can use instead the existing withContext function:
// this suspends until the code inside is done
withContext(Dispatchers.IO) {
// run some blocking IO code
}
However, note that this is independent from defining concurrent code. If you want to run multiple things concurrently, even with suspend functions, you'll need coroutine builders such as async or launch like in the above code.

NonCancellable coroutine gets cancelled

I'm trying to experiment with non-cancellable coroutines and I wrote the following code:
fun main(): Unit = runBlocking {
// launch first coroutine
coroutineScope {
val job1 = launch {
withContext(NonCancellable) {
val delay = Random.nextLong(from = 500, until = 5000)
println("Coroutine started. Waiting for ${delay}ms")
delay(delay)
println("Coroutine completed")
}
}
delay(300) // let it print the first line
println("Cancelling coroutine")
job1.cancelAndJoin()
}
}
Output:
Coroutine started. Waiting for 1313ms
Cancelling coroutine
Coroutine completed
So far, everything works as expected. However, if I pass the NonCancellable context (or rather, Job) directly in the launch function, the behaviour changes and the coroutine is cancelled:
fun main(): Unit = runBlocking {
// launch first coroutine
coroutineScope {
val job1 = launch(context = NonCancellable) {
val delay = Random.nextLong(from = 500, until = 5000)
println("Coroutine started. Waiting for ${delay}ms")
delay(delay)
println("Coroutine completed")
}
delay(300) // let it print the first line
println("Cancelling coroutine")
job1.cancelAndJoin()
}
}
Output:
Coroutine started. Waiting for 4996ms
Cancelling coroutine
Why is the second snippet producing a different output?
The job you pass as an argument to a launch method is not the job of the launched coroutine, but its parent job.
In the second snippet above, NonCancellable is the parent job of the job1. As the job1 is just a normal job, it's cancellable (but its parent isn't).

Differences between two coroutine launch in kotlin

What's the difference between CoroutineScope(dispatchers).launch{} and coroutineScope{ launch{}}?
Say I have the code below:
(you can go to Kotlin playground to run this snippet https://pl.kotl.in/U4eDY4uJt)
suspend fun perform(invokeAfterDelay: suspend () -> Unit) {
// not printing
CoroutineScope(Dispatchers.Default).launch {
delay(1000)
invokeAfterDelay()
}
// will print
coroutineScope {
launch {
delay(1000)
invokeAfterDelay()
}
}
}
fun printSomething() {
println("Counter")
}
fun main() {
runBlocking {
perform {
printSomething()
}
}
}
And as the comment stated, when using CoroutineScope().launch it won't invoke the print, however when using the other way, the code behaves as intended.
What's the difference?
Thanks.
Further question
New findings.
if I leave the perform function like this (without commenting out one of the coroutines)
suspend fun perform(invokeAfterDelay: suspend () -> Unit) {
CoroutineScope(Dispatchers.Default).launch {
delay(1000)
invokeAfterDelay()
}
coroutineScope {
launch {
delay(1000)
invokeAfterDelay()
}
}
}
then both of these coroutines will be executed 🤔Why?
CoroutineScope().launch {} and coroutineScope { launch{} } have almost nothing in common. The former just sets up an ad-hoc scope for launch to run against, completing immediately, and the latter is a suspendable function that ensures that all coroutines launched within it complete before it returns.
The snippet under your "Further question" is identical to the original one except for the deleted comments.
Whether or not the first coroutine prints anything is up to non-deterministic behavior: while perform is spending time within coroutineScope, awaiting for the completion of the inner launched coroutine, the first one may or may not complete itself. They have the same delay.

Kotlin coroutines barrier: Wait for all coroutines to finish

I need to start many coroutines in a for loop and get a callback in the Main thread after all the tasks are completed.
What is the best way to do this?
//Main thread
fun foo(){
messageRepo.getMessages().forEach {message->
GlobalScope.launch {
doHardWork(message)
}
}
// here I need some callback to the Main thread that all work is done.
}
And there is no variant to iterate messages in CoroutineScope. The iteration must be done in the Main thread.
You can wait until all tasks will be completed with awaitAll and then execute your callback in the main thread with withContext
fun foo() {
viewModelScope.launch {
messageRepo.getMessages().map { message ->
viewModelScope.async(Dispatchers.IO) {
doHardWork(message)
}
}.awaitAll()
withContext(Dispatchers.Main) {
// here I need some callback that all work is done.
}
}
}