Launching Your Coroutine Knowledge
Launching Your Coroutine Knowledge
In this article, we discuss some fundamentals behind coroutines in Kotlin to increase application performance.
Join the DZone community and get the full member experience.Join For Free
Coroutines are essentially lightweight threads that allow your applications to squeeze more juice out of the machines they are running on, without needing to manually manage the threads executing them. Coroutines add a new abstraction, further removing you (the developer) away from the lifecycle of threads. This abstraction allows a single thread to context switch between various tasks and therefore making better use of the thread’s time. It spends less time sitting around awaiting a result (from a potentially slow I/O task) and can instead continue to be useful by working on a task issued by another coroutine.
Let’s try thinking of a real-life example. Think of your day as a developer. You build your code, and it takes, let’s say, 3 minutes to do so. During that time, what do you do? Well, yes, of course, some times you might just sit there and literally do nothing. But most of the time, you’ll check your emails, Slack, Twitter, or go for a stretch. It doesn’t really matter what you’re doing, it’s the fact that you are doing something else. You didn’t just sit there and wait for the build to finish. You saturated your own processing and did something else while you waited. That is the concept that coroutines provide.
Yes, I did just compare you to a thread. You spawn, you live, and you die. Some of you are even killed… Ok, that metaphor got a bit dark.
One last comment before we dive into a small example. The concept of lightweight threads that coroutines introduce to Kotlin is not new. Many other languages include this ability, for example.
Coroutines are not included in Kotlin’s standard library (although some keywords are baked in). You will need to add a dependency on
kotlinx-coroutines-core to access coroutines.
The version will change as the library is updated (yes, I have to say this…)
A Simple Coroutine
As this is an introduction to coroutines, we will only go over an example with limited scope. I want to point out that a lot of the benefits of coroutines will not be highlighted by these examples. Moving forward, I will cover these topics which will show you more interesting use-cases.
Let’s jump right in:
In this example, we have some code that launches a coroutine with a
delay before continuing to print out a message. Even in this short example, there are a few things we can extract from it. Let’s start from the top and work our way down.
runBlocking- Runs a new coroutine that blocks the current thread until everything inside of it (including other coroutines) has completed. This is only here to make the example work. Realistically, you should only use this to move from blocking to non-blocking code, such as in
main(like the example) or in tests.
launch- Launches a new coroutine. Unlike
launchdoes not block the current thread. Furthermore, it inherits the
CoroutineContextfrom the current
CoroutineScope, these magic words will be expanded on later.
delay- Suspends the coroutine it is executed in for a given amount of time. In one way it is similar to
Thread.sleepin that it stops the coroutine from continuing for a while. But, it does a lot more than that. The thread that the coroutine is running on does not get blocked (like a
Thread.sleepwould). Instead, the coroutine suspends. Releasing its thread to allow another coroutine to continue executing. Once the
delayperiod is up, and a thread is available, the coroutine will continue where it left off.
All functions shown above are part of
Quite a few words and concepts were thrown just thrown at you. I would be a monster to not go through them.
- Suspending a coroutine - Conceptually, a coroutine suspending is like the passing of a baton in a never-ending relay race, allowing the next runner to progress. As you move into a multi-threaded environment though, this example becomes a bit weird as everyone is passing the baton to semi-random runners, but let’s ignore that. Coroutines share threads (for example, from a thread pool). Suspending a coroutine frees up the thread it was executing on to allow another to take over. This is the main benefit of coroutines. Allowing the number of running coroutines to significantly surpass the number of threads available to the application.
CoroutineContext- The persistent context of a coroutine, that decides what threads the asynchronous code found inside of it executes on.
CoroutineScope- Consists of a single
CoroutineContextproperty that is provided to any new (child) coroutines that are launched inside it. Most functions that create new coroutines are extension functions of
CoroutineScope, therefore requiring a scope to before anything useful can be done. In the example above,
runBlockingis providing its
CoroutineScopeto the following call to
This blog post by Roman Elizarov (Team Lead for Kotlin libraries) goes into the differences between
The topic of suspending a coroutine was touched on above, in this section, we will cover the
You have already seen one suspending function from the example above,
delay. Below are the contents of
The function is short enough that I’ve included all of its contents, but don’t worry about understanding it all. We are focusing on the
suspend keyword. Notice that I said keyword.
suspend is built into the Kotlin language itself.
So, why do we need it in the first place?
All functions that suspend at some point during their execution must be annotated with
suspend. I mean duh, right? Trying to go deeper than this to fully understand when a function suspends can quickly become confusing. Quite frankly, I haven’t use coroutines enough yet to give you a more in-depth explanation.
So for now, remember this is a getting started post of sorts and follow this rule of thumb. If you call a function prefaced with
suspend, and the calling point is not inside a coroutine, then your function should also be marked with
Calling point outside a coroutine:
Calling point inside a coroutine:
Both of these are a slight refactoring of the original example.
The compiler will help you in a lot of situations (a benefit of being a keyword). For example, trying to compile:
Note that the suspend is missing
Will lead to the following error:
Which pretty much reiterates what I said previously.
I believe that should be enough of an explanation to get you started.
Kotlin’s coroutines are lightweight threads that share and release their underlying resources whenever a single coroutine reaches a suspension point. This allows better utilization of an application’s resources, as threads are not necessarily blocked when performing long-running tasks. We looked at a rudimentary example consisting of a single coroutine and a suspending function. Finally, we took a modest look at the
suspend keyword and how you add it to functions that suspend.
The real benefits of coroutines have not been touched on through the examples in this post. Instead, it has focused on laying the groundwork as you begin building your coroutine knowledge. Moving forward, I aim to publish more content on Kotlin coroutines. So look out for those, assuming I keep my word…
If you enjoyed this post or found it helpful (or both) then please feel free to follow me on Twitter at @LankyDanDev and remember to share with anyone else who might find this useful!
Published at DZone with permission of Dan Newton , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.