Hello, coroutine world!

Now, let's rewrite our Hello World application with coroutines.

But, hey! What is a coroutine? Basically, a coroutine is a very light thread that runs a block of code and has a similar life cycle, but can complete with a return value or an exception. Technically, a coroutine is an instance of a suspendable computation, a computation that may suspend. Coroutines aren't bound to a particular thread and can suspend in one Thread and resume execution in a different one:

import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking

fun main(args: Array<String>) = runBlocking {
launch {
delay(1000)
println("World")
}
print("Hello ")
delay(2000)
}

There are a few things to cover here:

  • runBlocking: This function creates a coroutine and blocks the current Thread until the coroutine finishes, returning its result value (Unit in this case).
  • launch: This function creates a new coroutine without blocking the current Thread and returns Job (ignored here).
  • delay: This function is a suspending (more on this later) function that delays the current coroutine without blocking the current thread.
  • suspend: A suspending function is a function that may suspend the execution of a coroutine, without blocking the current Thread; therefore a suspending function must be called inside a coroutine—it can't be invoked from normal code. The function must be marked with the suspend modifier. So, delay can be invoked inside runBlocking and launch, both functions (among others) take a suspending lambda as the last parameter—a suspending lambda is a lambda marked with the suspend modifier.

Let's summarize what we know now, and a few other concepts before going further:

Concept

Description

Coroutine

A very light thread that can return a value and can suspend and resume.

Suspending function

A function marked with a suspend modifier. It can suspend a coroutine without blocking the thread. Suspending functions must be invoked inside a coroutine, for example delay.

Suspending lambda

A lambda function marked with a suspend modifier. It can suspend a coroutine without blocking the thread.

Coroutine builder

A function that takes a suspending lambda, creates a coroutine and may return a result, for example runBlocking.

Suspension point

A point where a suspending function is invoked.

Continuation

The state of a suspended coroutine at a suspension point, it represents the rest of its execution after suspension point.

Let's get back to business.

As we discussed previously, computations can have different execution times. So, delay isn't ideal in our Hello World example:

fun main(args: Array<String>) = runBlocking {
val job = launch {
delay(1000)
println("World")
}
print("Hello ")
job.join()
}

As with our example with threads, we take the reference to the job created by launch, and we suspend it at the end with the suspending function join.

So far, so good. But are coroutines so very light? Can we have 10,000 coroutines?

Let's try it by executing the following code snippet:

fun main(args: Array<String>) = runBlocking {
val jobs = List(10000) {
launch {
delay(1000)
print('.')
}
}
jobs.forEach { job -> job.join() }
}
}

Oh, indeed! It works:

They are orders of magnitude faster than the Executor solution, a lot less memory, fewer threads (barely seven threads) and, on top of that, are very easy to read.

Let's go with 1 million coroutines:

Less than 2,000 threads need more than 1.5 GB of memory. 1 million coroutines need less than 700 MB of memory—I rest my case. The verdict is that the coroutines are very, very light.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.22.23