Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between thread and coroutine in Kotlin

Is there any specific language implementation in Kotlin, which differs it from another languages implementation of coroutines?

  • What means that coroutine is like light-weight thread?
  • What is the difference?
  • Are kotlin coroutines actually running in parallely / concurrently?
  • Even in multi-core system, there is only one coroutine running at any given time (is it right?)

Here I'm starting 100000 coroutines, what happens behind this code?

for(i in 0..100000){    async(CommonPool){     //run long running operations   } } 
like image 971
Jemo Mgebrishvili Avatar asked Mar 25 '17 20:03

Jemo Mgebrishvili


1 Answers

What means that coroutine is like light-weight thread?

Coroutine, like a thread, represents a sequence of actions that are executed concurrently with other coroutines (threads).

What is the difference?

A thread is directly linked to the native thread in the corresponding OS (operating system) and consumes a considerable amount of resources. In particular, it consumes a lot of memory for its stack. That is why you cannot just create 100k threads. You are likely to run out of memory. Switching between threads involves OS kernel dispatcher and it is a pretty expensive operation in terms of CPU cycles consumed.

A coroutine, on the other hand, is purely a user-level language abstraction. It does not tie any native resources and, in the simplest case, uses just one relatively small object in the JVM heap. That is why it is easy to create 100k coroutines. Switching between coroutines does not involve OS kernel at all. It can be as cheap as invoking a regular function.

Are kotlin's coroutines actually running in parallely / concurrently? Even in multi-core system, there is only one coroutine running at any given time (is it right?)

A coroutine can be either running or suspended. A suspended coroutine is not associated to any particular thread, but a running coroutine runs on some thread (using a thread is the only way to execute anything inside an OS process). Whether different coroutines all run on the same thread (a thus may use only a single CPU in a multicore system) or in different threads (and thus may use multiple CPUs) is purely in the hands of a programmer who is using coroutines.

In Kotlin, dispatching of coroutines is controlled via coroutine context. You can read more about then in the Guide to kotlinx.coroutines

Here I'm starting 100000 coroutines, what happens behind this code?

Assuming that you are using launch function and CommonPool context from the kotlinx.coroutines project (which is open source) you can examine their source code here:

  • launch is defined here https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
  • CommonPool is defined here https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt

The launch just creates new coroutine, while CommonPool dispatches coroutines to a ForkJoinPool.commonPool() which does use multiple threads and thus executes on multiple CPUs in this example.

The code that follows launch invocation in {...} is called a suspending lambda. What is it and how are suspending lambdas and functions implemented (compiled) as well as standard library functions and classes like startCoroutines, suspendCoroutine and CoroutineContext is explained in the corresponding Kotlin coroutines design document.

like image 177
Roman Elizarov Avatar answered Oct 06 '22 00:10

Roman Elizarov