The Programmers Guide To Kotlin - Coroutines
Written by Mike James   
Monday, 07 June 2021
Article Index
The Programmers Guide To Kotlin - Coroutines
Launch
coroutineScope

Blocking With coroutineScope

Notice that launch is a non-blocking call that creates and adds a coroutine to the queue. If you want to create a blocking call to create and add a coroutine then use the coroutineScope function. This creates a new scope that only unblocks when all of the coroutines that have been created within it have finished.

For example, if you change launch to coroutineScope:

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
fun main() { println("main start")
runBlocking { println("Coroutine1 start") coroutineScope { co2()
} for (i in 1..20) { print(i) delay(1) } println(" Coroutine1 finishing") } println("main stopped") }
suspend fun co2() { println("Coroutine2 start") for (i in 1..10) { print(i) delay(1) } println(" Coroutine2 finishing") }

you will see:

main start
Coroutine1 start
Coroutine2 start
12345678910 Coroutine2 finishing
1234567891011121314151617181920 Coroutine1 finishing
main stopped

Notice that now Coroutine1 is suspended until Coroutine2 has finished even if Coroutine2 suspends itself repeatedly for 1ms. This is the idea of structured asynchronous code. Each coroutineScope block does not move on until all of the coroutines it contains have finished. You can also use the block to cancel, or otherwise modify, all of the coroutines it contains and if a contained coroutine fails then the entire block fails, more of this later. You can use coroutineScope blocks to organize and control your coroutines.

At this point you might be wondering what the difference is between coroutineScope and runBlocking? The answer is that you can use runBlocking from a non-suspending, i.e. a standard function, but you can only use coroutineScope from within another suspending function. In other words, you can use runBlocking to get coroutines started from the main function, but after that you should use coroutineScope to repurpose the thread.

Sequential & Concurrent

Now we have two ways to run coroutines – launch which places a coroutine in the dispatcher’s queue and returns, and coroutineScope which does the same but then waits until the coroutine has completed. This means you can now schedule coroutines to run sequentially or concurrently.

In the case of sequentially:

runBlocking {
    coroutineScope {
        co1()
    }
coroutineScope { co2() } }

the outer coroutine places co1 and then co2 into the queue. The co1 function is run when the outer coroutine ends and runs to completion, even if it is suspended multiple times, before the co2 function is run. The runBlocking doesn’t return until both co1 and co2 have completed. If the two functions being called are marked as suspend you can drop the use of coroutineScope and simply call the functions:

runBlocking {
    co1()
    co2()
}

This works because both functions can contain suspension points and are therefore run asynchronously even if they occupy a single position in the dispatcher’s queue. That is, co2 will not run until co1 has finished even if it suspends. This form is slightly more efficient but only works with functions that have the suspend modifier and the functions are treated as a single job.

The alternative is concurrently:

runBlocking {
    launch {
        co1()
    }
    launch {
        co2()
    }
}

In this case co1 and co2 are added to the dispatcher’s queue as before and in a single threaded dispatcher co1 is started when the outer coroutine is finished. The difference is that now co2 is started is co1 suspends and co1 only restarts if co2 suspends or completes. When the runBlocking is finished you can say that co1 and co2 are complete but not the order in which they finished.

In book but not in this extract:

  • Dispatchers & Threads
  • Shared Resources
  • Async, Job & Defer
  • Canceling Coroutines
  • Flows
  • Channels

 

Summary

  • A function defined with the suspend qualifier can have suspension points, places where its execution can be suspended and resumed at a later date.

  • All suspendable functions have to be run in a CoroutineScope which only terminates when all of the coroutines it contains finish or fail.

  • You can create a CoroutineScope using runBlocking which uses Main, i.e. the UI thread, to run any of the coroutines defined within it. As the Main thread is used, runBlocking blocks the program that calls it.

  • You can use delay or yield to create suspension points within a coroutine.

  • Coroutines run to completion unless they are suspended or canceled.

  • The launch method adds coroutines to the dispatcher’s queue and continues. How the coroutine is run depends on the dispatcher.

  • The coroutineScope method adds a coroutine to the dispatcher’s queue and waits until it finishes.

  • The behavior of coroutines depends on the dispatcher used. Main dispatcher is a single-threaded dispatcher generally used to update the UI. Default dispatcher has at least two threads and is generally used for CPU intensive tasks and the IO dispatcher typically has 64 threads and is recommended for non-CPU intensive IO tasks.

  • Sharing resources between coroutines is tricky unless you restrict yourself to a single thread. If not you need to use atomic operations or locks.

  • If you want a coroutine to return a result use the async method and await the result on the defer object it returns.

  • Coroutines that have suspension points are generally canceled automatically. If not you need to test the isActive property and stop the coroutine manually.

  • Flows are asynchronous for loops in function form.

  • Channels are asynchronous communication “pipes” between coroutines.

This article is an extract from: 

Programmer's Guide To Kotlin Third Edition

kotlin3e360

You can buy it from: Amazon

Contents

  1. What makes Kotlin Special
  2. The Basics:Variables,Primitive Types and Functions 
  3. Control
         Extract: If and When 
  4. Strings and Arrays
  5. The Class & The Object ***NEW!
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
        Extract: Iterators & Sequences 
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
        Extract: Destructuring 
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java
        Extract: Using Swing
  16. Compose Multiplatform
        Extract: Compose Layout 

<ASIN:B0D8H4N8SK>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Gender Differences In Coding Style
13/11/2024

A novel investigation into the gender gap between men and women regarding coding ability was undertaken by Dr Siân Brooke. Her conclusion? There is a difference in the Python code [ ... ]



Apollo Adds REST APIs For GraphQL
29/10/2024

Apollo has added a simpler way to integrate REST APIs into a federated GraphQL environment. Available now in public preview, can be used to map REST API endpoints to their GraphQL schema using a decla [ ... ]


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 07 June 2021 )