Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for all coroutines to finish?

I'm launching a coroutine and I want it to finish before I resume execution of main thread.

My code simplified looks like this:

fun hello() {
    for (i in 0..100) {
        println("hello")
    }
}

fun main(args: Array<String>) {
    val job = GlobalScope.launch { hello() } //launch parallel 
    GlobalScope.launch { job.join() }  //try to wait for job to finish
    print("done")
}

The problem is, because job.join() needs to be within a coroutine, the main line of execution is deferred to "done", so the output looks like this:

donehello
hello
hello
hello

I want to wait for the job to complete, like using sync.WaitGroup in Go. So my output would deterministically look like this:

hello
hello
hello
hello
...
done

How do I accomplish this?

like image 566
nz_21 Avatar asked Apr 29 '19 13:04

nz_21


People also ask

What does await do in coroutines?

So this is the object of deferred then we do whatever processing we need in that program. Then we actually need the value we have to await() function. So this will pause the execution until the value is available for our main program. So we are simply waiting for a value to be available.

How many coroutines can be executed at once?

A thread can only execute one coroutine at a time, so the framework is in charge of moving coroutines between threads as necessary.

How coroutines are suspended?

Which coroutine gets suspended? The outer async starts a coroutine. When it calls computation() , the inner async starts a second coroutine. Then, the call to await() suspends the execution of the outer async coroutine, until the execution of the inner async 's coroutine is over.

Is async await coroutines?

The async/await pattern is built on two functions: async() to wrap the function call and the resulting value in a coroutine, and await() , which suspends code until the value is ready to be served.


1 Answers

Actually for your sample the job.join() is the way to ensure that at this point it waits until the given job is finished. Unfortunately you packed it inside a GlobalScope.launch again, which just puts that waiting in a background thread. Therefore it reaches the done earlier then you expect and on my machine it didn't even print any hello (but it could).

I assume you used launch because join can only be called from a coroutine or another suspending function? For your example it would have been sufficed to just add suspend to main, e.g.:

suspend fun main() {
  val job = GlobalScope.launch { hello() }
  job.join()
  print("done")
}

or you could have used runBlocking and wrap the main with it, e.g.:

fun main() = runBlocking {
  val job = launch { hello() }      
  job.join()
  print("done")
}

Now the disclaimer... you may want to consult the following sources before continuing (if you didn't do already):

  1. Coroutine basics
  2. The reason to avoid GlobalScope by Roman Elizarov
  3. Concurrent coroutines - Concurrency is not parallelism by @s1m0nw1
like image 96
Roland Avatar answered Oct 13 '22 15:10

Roland