What is a Coroutine in Kotlin? Which are the differences with threads? What is collaborative concurrency? Have a look at these slides and at the companion Github repository https://github.com/f-lombardo/kotlin-from-scratch
by allowing execution to be suspended and resumed. Coroutines are very similar to threads. However, coroutines are cooperatively multitasked, whereas threads are typically preemptively multitasked. This means that coroutines provide concurrency but not parallelism. (Wikipedia) Subroutines: objects defined by the programming language, not by the operating system.
dependency to org.jetbrains.kotlinx:kotlinx-coroutines-core:version_nbr 2) There is a system property to set if you want to see more coroutine infos System.setProperty("kotlinx.coroutines.debug", "")
{ while (true) { logMsg("Bischerata numero ${bigPrime()}") } } async(CoroutineName("Buffalmacco")) { while (true) { logMsg("Calandrino è bischero ${bigPrime()} volte!") } } } } It doesn’t work because Calandrino keeps the thread!
{ while (true) { logMsg("Bischerata numero ${bigPrime()}") } } async(CoroutineName("Buffalmacco")) { while (true) { logMsg("Calandrino è bischero ${bigPrime()} volte!") } } } } It doesn’t work because Calandrino keeps the thread! (Bischero!)
Random()) yield() } } Collaborating functions are suspending ones The suspend modifier does not make a function either asynchronous or non-blocking. It just tells that the function could be suspended, as it could call other suspending functions (such as yield or delay). Anyway, it’s a good convention to create suspending functions that don’t block the calling thread.
{ bigPrime(1024) } The async and launch coroutines builders are extension functions for the CoroutineScope interface, so they must be called on an object of that type. When you see them called without an explicit scope, as inside runBlocking, it is because they are called in a block that provides an implicit this receiver of type CoroutineScope.
{ bigPrime(1024) } A CoroutineScope holds information on how to run coroutines, using a CoroutineContext object. We can create one of them with: • the CoroutineScope factory function; • the MainScope factory function: this works for Android, Swing and JavaFx applications, pointing to the Main UI thread; • the GlobalScope object, for top-level coroutines which are operating on the whole application lifetime; • the runBlocking function, that creates an implicit CoroutinesScope receiver; • the coroutineScope function (inside a suspend function/coroutine).
{ bigPrime(1024) } A CoroutineContext is something like a “set” of properties related to the coroutine. Each element of a CoroutineContext is a CoroutineContext too. Elements can be added with the + operator. So, whenever we have a CoroutineContext parameter, we can pass just one type of information, that will override the existing one, or a concatenated set of elements.
a CoroutineContext parameter, we can pass just one type of information, that will override the existing one, or a concatenated set of elements. The type of these three expression is CoroutineContext: • Dispatchers.Unconfined • CoroutineName("Calandrino") • Dispatchers.Unconfined + CoroutineName("Calandrino") CoroutineScope(Dispatchers.Unconfined + CoroutineName("Calandrino")).async { bigPrime(1024) }
{ bigPrime(1024) } Types of dispatchers: • Dispatcher.Default: backed by a shared pool of threads with maximum size equal to the number of CPU cores; for CPU/bound tasks; • Dispatcher.IO: for offloading blocking IO tasks to a shared pool of threads (the default is 64); • Dispatcher.Main: main UI thread in Android/Swing applications; • Dispathcer.Unconfined: not confined to any specific thread. The coroutine executes in the current thread first and lets the coroutine resume in whatever thread.
{ bigPrime(1024) } Dispatcher can also be created with these functions: • newSingleThreadContext: one thread per coroutine; • newFixedThreadPoolContex: a fixed size thread pool; • asCoroutineDispatcher extension function on Java Executor.
job: Job = launch(Dispatchers.Unconfined) { ConsoleProgressBar().showContinuously() } println("A big prime number: ${bigPrime(2048)}") job.cancel() } The launch builder returns a Job that can be used to control the execution of the coroutine.
all their children Cancellation of a parent leads to immediate cancellation of all its children fun main(args: Array<String>) = runBlocking { val job: Job = launch(Dispatchers.Unconfined) { launch { launch { delayAndLog() } delayAndLog() } delayAndLog() } println("Here is a big prime number: ${bigPrime(2048)}") job.cancel() }