Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Grokking Coroutines (MinneBar)

Daniel Lew
October 13, 2020

Grokking Coroutines (MinneBar)

What are coroutines, really?

Given at MinneBar 2020.

Daniel Lew

October 13, 2020
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. Grokking Coroutines
    @danlew42

    View full-size slide

  2. “Essentially, coroutines are light-weight
    threads.”
    ~Kotlin Documentation

    View full-size slide

  3. myScope.launch {
    val image = downloadImage()
    displayImage(image)
    }
    suspend fun downloadImage() = withContext(Dispatchers.IO) { … }
    fun displayImage(image: Image)

    View full-size slide

  4. sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }

    View full-size slide

  5. What are coroutines?

    View full-size slide

  6. Subroutines
    fun sumSquaredValues(values: List): Int {
    return values.sumBy { value ->
    square(value)
    }
    }
    fun square(value: Int): Int = value * value

    View full-size slide

  7. Routine Subroutine

    View full-size slide

  8. Coroutine Coroutine

    View full-size slide

  9. val iterator = sequence.iterator()
    while (iterator.hasNext()) {
    val next = iterator.next()
    println(next)
    }
    sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    Consumer Producer

    View full-size slide

  10. val iterator = sequence.iterator()
    while (iterator.hasNext()) {
    val next = iterator.next()
    println(next)
    }
    sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    Consumer Producer

    View full-size slide

  11. class Fibonacci {
    var cur = 1
    var next = 1
    fun next(): Int {
    val toReturn = cur
    val tmp = cur + next
    cur = next
    next = tmp
    return toReturn
    }
    }
    sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    Versus

    View full-size slide

  12. Multiple Suspend Points
    sequence {
    yield(1) // first Fibonacci number
    var cur = 1
    var next = 1
    while (true) {
    yield(next) // next Fibonacci number
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }

    View full-size slide

  13. Why use coroutines?

    View full-size slide

  14. Why use coroutines?
    • Another tool for writing good code

    • Two ways to use tool:

    • Code Structure

    • Concurrency

    View full-size slide

  15. https://www.flickr.com/photos/117693452@N04/12547460924

    View full-size slide

  16. https://www.maxpixel.net/Lana-Frame-Threads-Thread-Handmade-Weaving-3019254

    View full-size slide

  17. Blocking Functions
    fun main() {
    print(calculateMeaningOfLife())
    }
    fun calculateMeaningOfLife(): Int {
    // Calculates for 7.5 million years, then...
    return 42
    }

    View full-size slide

  18. Non-Blocking Function
    suspend fun calculateMeaningOfLife(): Int {
    delay(7.5 million years)
    return 42
    }

    View full-size slide

  19. Blocking vs. Nonblocking
    Blocking Blocked During I/O
    Non-Blocking
    Waiting During I/O
    Doing other work

    View full-size slide

  20. Two Tasks, One Thread
    suspend fun task(name: String, delay: Long) {
    joinAll(
    async { doSomething("First", 10) },
    async { doSomething("Second", 5) }
    )
    }
    suspend fun doSomething(name: String, delay: Long) {
    println("$name START (${Thread.currentThread().name})")
    delay(delay)
    println("$name END (${Thread.currentThread().name})")
    }
    First START (main)
    Second START (main)
    Second END (main)
    First END (main)

    View full-size slide

  21. Threads == preemptive multitasking
    Coroutines == cooperative multitasking

    View full-size slide

  22. Callback Hell
    requestRandomUrl { url ->
    downloadImage(url) { image ->
    displayImage(image)
    }
    }
    myScope.launch {
    val url = requestRandomUrl()
    val image = downloadImage(url)
    displayImage(image)
    }
    Vs

    View full-size slide

  23. Why use coroutines?
    • Concurrency

    • Coroutines *are* like light-weight threads!

    • …In any language!

    • Code Structure

    • Coroutines enable simpler code

    View full-size slide

  24. Kotlin Coroutines

    View full-size slide

  25. kotlin.coroutines
    (stdlib)
    kotlinx.coroutines
    (library)

    View full-size slide

  26. kotlin.coroutines
    (stdlib)
    kotlinx.coroutines
    (library)

    View full-size slide

  27. Stdlib Basics
    • Suspending functions

    • A way to start suspending functions

    • Contexts

    View full-size slide

  28. Suspend Keyword

    View full-size slide

  29. Start Suspending Functions
    suspend fun mySuspendingFunction()
    fun (suspend () -> T).startCoroutine(completion: Continuation)
    fun main() {
    ::mySuspendingFunction.startCoroutine(
    Continuation(EmptyCoroutineContext) { }
    )
    }

    View full-size slide

  30. Coroutine Context
    • Store useful info about coroutine

    • Dispatcher

    • Job

    • Debug info

    View full-size slide

  31. Coroutine Library

    View full-size slide

  32. myScope.launch {
    val image = downloadImage()
    displayImage(image)
    }
    suspend fun downloadImage() = withContext(Dispatchers.IO) { … }
    fun displayImage(image: Image)

    View full-size slide

  33. myScope.launch {
    val image = downloadImage()
    displayImage(image)
    }
    suspend fun downloadImage() = withContext(Dispatchers.IO) { … }
    fun displayImage(image: Image)

    View full-size slide

  34. myScope.launch {
    val image = downloadImage()
    displayImage(image)
    }
    suspend fun downloadImage() = withContext(Dispatchers.IO) { … }
    fun displayImage(image: Image)

    View full-size slide

  35. myScope.launch {
    val image = downloadImage()
    displayImage(image)
    }
    suspend fun downloadImage() = withContext(Dispatchers.IO) { … }
    fun displayImage(image: Image)

    View full-size slide

  36. Review
    • Coroutines are suspending functions

    • Suspending functions are useful for concurrency

    • Kotlin stdlib provides coroutine support

    • Kotlin coroutine library adds practical functions for coroutines

    View full-size slide

  37. More Info
    • Longer Talk: https://youtu.be/Axq8LdQqQGQ

    • Links: github.com/Kotlin/kotlinx.coroutines/#documentation

    • Roman Elizarov: medium.com/@elizarov

    • Android + Coroutines: medium.com/@objcode

    • Structured concurrency: vorpus.org/blog/notes-on-structured-
    concurrency-or-go-statement-considered-harmful/

    View full-size slide