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

Grokking Coroutines

Daniel Lew
October 05, 2019

Grokking Coroutines

What are coroutines, really?

Given at Kotlin/Everywhere MN.

Recording available here: https://www.youtube.com/watch?v=Axq8LdQqQGQ

Daniel Lew

October 05, 2019
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … } suspend fun

    downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
  2. sequence { var cur = 1 var next = 1

    while (true) { yield(cur) val tmp = cur + next cur = next next = tmp } }
  3. –The Design of Everyday Things “A good conceptual model allows

    us to predict the effects of our actions. Without a good model we operate by rote, blindly; we do operations as we were told to do them; we can’t fully appreciate why, what effects to expect, or what to do if things go wrong.”
  4. Subroutines fun sumSquaredValues(values: List<Int>): Int { return values.sumBy { value

    -> square(value) } } fun square(value: Int): Int = value * value
  5. 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
  6. 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
  7. 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
  8. 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 } }
  9. Why use coroutines? • Another tool for writing good code

    • Two ways to use tool: • Generators • Concurrency
  10. 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)
  11. Not Basic suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … }

    suspend fun downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
  12. fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) suspend fun mySuspendingFunction()

    fun main() { ::mySuspendingFunction.startCoroutine( Continuation(EmptyCoroutineContext) { } ) }
  13. fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) suspend fun mySuspendingFunction()

    fun main() { ::mySuspendingFunction.startCoroutine( Continuation(EmptyCoroutineContext) { } ) }
  14. Continuations public interface Continuation<in T> { public val context: CoroutineContext

    public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: Any? )
  15. Continuations public interface Continuation<in T> { public val context: CoroutineContext

    public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: Any? )
  16. Continuations public interface Continuation<in T> { public val context: CoroutineContext

    public fun resumeWith(result: Result<T>) } class Result<out T> constructor( val value: T?, val exception: Throwable )
  17. Continuations public interface Continuation<in T> { public val context: CoroutineContext

    } fun <T> Continuation<T>.resume(value: T) fun <T> Continuation<T>.resumeWithException(exception: Throwable)
  18. Coroutines - Under the Hood suspend fun fizzBuzz(): String |

    | compiles to… V fun fizzBuzz(continuation: Continuation<String>): Any?
  19. Coroutines - Under the Hood suspend fun fizzBuzz(): String |

    | compiles to… V fun fizzBuzz(continuation: Continuation<String>): Any?
  20. Callback Hell requestRandomUrl { url -> downloadImage(url) { image ->

    displayImage(image) } } myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) } Vs
  21. Coroutine Basics • suspend keyword • Continuations • CoroutineContexts •

    …And a couple other minor things not worth mentioning here
  22. What We Want suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { …

    } suspend fun downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
  23. Starting a Coroutine suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { …

    } suspend fun downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
  24. val job1 = launch { task1() } val job2 =

    launch { task2() } val job3 = launch { task3() } ... job1.cancel() job2.cancel() job3.cancel()
  25. val job1 = launch { task1() } val job2 =

    launch { task2() } val job3 = launch { task3() } ... job1.cancel() job2.cancel() job3.cancel()
  26. val job = Job() val myScope = CoroutineScope(job) ... myScope.launch

    { task1() } myScope.launch { task2() } myScope.launch { task3() } ... job.cancel()
  27. val job = Job() val myScope = CoroutineScope(job) ... myScope.launch

    { task1() } myScope.launch { task2() } myScope.launch { task3() } ... job.cancel()
  28. val job = Job() val myScope = CoroutineScope(job) ... myScope.launch

    { task1() } myScope.launch { task2() } myScope.launch { task3() } ... job.cancel()
  29. class MyActivity : Activity() { private val job = Job()

    private val myScope = CoroutineScope(job) /* ...Launch coroutines to your heart's desire... */ override fun onDestroy() { super.onDestroy() job.cancel() } }
  30. Structured Concurrency • Concurrency with boundaries • Forces you to

    do the right thing • Explainer: https://vorpus.org/blog/notes-on-structured-concurrency-or- go-statement-considered-harmful/
  31. What about threads? • Don’t want to run everything on

    a single thread • Writing everything non-blocking is exhausting • Can’t take advantage of multiple cores • Sometimes illegal! • Remember CoroutineContext? • Store whatever you want • Idea: store the thread!
  32. Threading suspend fun requestRandomUrl() = withContext(Dispatchers.IO) { … } suspend

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

    fun downloadImage(url: Url) = withContext(Dispatchers.IO) { … } fun displayImage(image: Image) myScope.launch { val url = requestRandomUrl() val image = downloadImage(url) displayImage(image) }
  34. Clarifying Threading • Metaphor: Coroutines are like light-weight threads •

    Feature: Kotlin coroutines let you choose thread for coroutines
  35. Review • Coroutines are suspending functions • Suspending functions are

    useful for concurrency • Kotlin stdlib provides coroutine support • Kotlin coroutine library adds practical functions for coroutines
  36. But Wait, There’s More! • Compatibility - How do you

    use asynchronous non-coroutine code with coroutines? • Composition - How do you concurrently execute multiple suspending functions? • Cancellation - How do coroutines actually stop? • Exception handling - What happens when something goes wrong? • Flow - How can coroutines return multiple values asynchronously? • Channels - How do coroutines communicate between threads safely? • Actors - ???
  37. More Info • 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/