Overview

We can think about Kotlin coroutines as lightweight threads. They allow us to execute code concurrently and are less resource-consuming than threads. Using coroutines can improve the performance, responsiveness, and scalability of our applications.
In this tutorial, we’ll explore different ways to run multiple coroutines in parallel in Kotlin.

Run Coroutines in Parallel Using launch()

Let’s take a look at how we can use the launch() function to start two coroutines in parallel:

Figure

Let’s break down all the important parts in the example:

  • coroutineScope(): creates a new coroutine scope and runs the specified block of code within that scope

    • If any child coroutine fails, this whole scope fails and all the child coroutines are canceled
    • It returns when the given code block and all child coroutines are completed, following the structured concurrency principle
  • launch(): launches a new coroutine that can be executed in parallel with other coroutines and returns a reference to the execution as a Job object

  • doSomething() and doSomethingElse(): wait for a random delay, without blocking the main thread, then log how long they waited for and return

Now, let’s execute the function and record how long it takes to complete:

Figure

Every time we execute this example, the output will be different - sometimes, doSomething() completes first:

Figure
And other times, doSomethingElse() finishes first:
Figure
Looking at the total execution time, we can clearly see that the coroutines were executed concurrently since the execution time for the whole example is significantly shorter than the execution times of the two coroutines combined.

The Job returned by launch() provides lots of functionalities for managing asynchronous code execution. For instance, it allows waiting for the completion or cancellation of the coroutine. All the details for the Job lifecycle can be found in the official Kotlin documentation.

Execute Coroutines in Parallel Using async()

In case we need to process the results, we can make use of the async method:

Figure

The function async() returns a Deferred. The coroutine will execute in the background, and we can obtain the actual return value with the await() function. Note that await() is a suspend function, so it won’t block the thread that is executing, and it will suspend the current coroutine until the result is actually available.

If we execute the code snippet:

Figure
The output will be:
Figure

Execute Many Coroutines in Parallel

If we need to execute multiple tasks in parallel and continue only when we have all the results, we can make use of the awaitAll() function.

Let’s take a look at an example that returns the list of all the numbers from 1 to 5 multiplied by themselves:

Figure

The output of the code above is:

Figure

All the coroutines are processed independently and concurrently, but then we’re able to wait for the results, thereby maintaining the original order.

Kotlin provides two implementations of awaitAll() - one as an extension function, as shown in the example above, and one that takes in a variable number of Deferred objects. The implementations are equivalent, but they may allow for cleaner code according to our use case.

Conclusion

In this article, we learned a few ways to run multiple coroutines in parallel. Coroutines are lightweight threads that allow us to execute concurrent code without blocking threads. This can improve the performance, responsiveness, and scalability of our applications.

We can use the launch() and async() functions to run coroutines in parallel. The launch() function returns a Job object that allows us to manage the coroutine, while the async() function returns a Deferred object that can be used to get the result of the coroutine when it is complete.

We can use the awaitAll() function to wait for the results of multiple coroutines in parallel. This is useful when we need to execute numerous similar tasks and then continue only when we have all the results.

Comments are closed on this article!