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

Kotlin Coroutines, Threads and Communication

Bob Dahlberg
February 04, 2020

Kotlin Coroutines, Threads and Communication

As the async and possibly parallel programming becomes easier, the risk of sharing mutable variables between coroutines arises.

When the boundaries are abstracted away, we should rely on safe ways to communicate between our coroutines. In this session, I will show how coroutines work with threads and both safe and unsafe forms of communication using coroutines.

Bob Dahlberg

February 04, 2020
Tweet

More Decks by Bob Dahlberg

Other Decks in Programming

Transcript

  1. Lightweight Coroutines Let’s test lightweight first. fun main() = runBlocking<Unit>

    { repeat(100_000) { launch { !// creates a coroutine println(“On thread !-> $thread”) } } }
  2. Lightweight Coroutines Let’s test lightweight first. repeat(100_000) { launch {

    !// creates a coroutine println(“On thread !-> $thread”) } } !// on main thread
  3. Lightweight Coroutines Let’s test lightweight first. !// on main thread

    repeat(100_000) { thread { !// creates a thread println(“On thread !-> $thread”) } }
  4. Lightweight Dispatchers Coroutines Let’s test lightweight first. !// on main

    thread repeat(100_000) { launch(Dispatchers.Default) { println(“On thread !-> $thread”) } }
  5. Lightweight Dispatchers Coroutines Let’s test lightweight first. !// on main

    thread repeat(100_000) { launch(Dispatchers.IO) { println(“On thread !-> $thread”) } }
  6. Lightweight Dispatchers Thread safety Coroutines Let’s take a detour around

    thread safety !// on main thread var i = 0 repeat(100_000) { launch(Dispatchers.Default) { i!++ } } println(“Result !-> $i”)
  7. Lightweight Dispatchers Thread safety Coroutines Let’s take a detour around

    thread safety !// on main thread var i = 0 repeat(100_000) { launch(Dispatchers.Default) { i!++ } } println(“Result !-> $i”) Output: Result !-> 85325
  8. Lightweight Dispatchers Thread safety Coroutines Let’s take a detour around

    thread safety !// on main thread var i = 0 repeat(100_000) { launch(Dispatchers.IO) { i!++ } } println(“Result !-> $i”) Output: Result !-> 78332
  9. Lightweight Dispatchers Thread safety Coroutines Let’s take a detour around

    thread safety !// on main thread var i = 0 repeat(100_000) { launch { i!++ } } println(“Result !-> $i”) Output: Result !-> 100000
  10. Lightweight Dispatchers Thread safety Coroutines Let’s take a detour around

    thread safety !// on main thread var i = 0 launch(Dispatchers.Default) { repeat(100_000) { launch { i!++ } } } println(“Result !-> $i”) Output: Result !-> 92342
  11. Lightweight Dispatchers Thread safety Threads? Coroutines Coroutines are threads !//

    on main thread launch(Dispatchers.Unconfined) { println(“A1. On thread $thread”) delay(200) println(“A2. On thread $thread”) } Output: A1. On thread main A2. On thread worker-1
  12. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    launch(Dispatchers.IO) { println(“A1. On thread $thread”) switchContext() println(“A2. On thread $thread”) } suspend fun switchContext() { withContext(Dispatchers.Default) { println(“B1. Switching $thread”) } } Output: A1. On thread worker-1 B1. Switching worker-1 A2. On thread worker-1
  13. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    launch(Dispatchers.IO) { println(“A1. On thread $thread”) switchContext() println(“A2. On thread $thread”) } suspend fun switchContext() { withContext(Dispatchers.Default) { println(“B1. Switching $thread”) } } Output: A1. On thread worker-1 B1. Switching worker-3 A2. On thread worker-3
  14. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    val local = ThreadLocal<String?>() launch(Dispatchers.IO) { local.set(“IO") println(“A1. ${local.get()}”) switchContext() println(“A2. ${local.get()}”) } suspend fun switchContext() { withContext(Dispatchers.Default) { println(“B1. ${local.get()}”) local.set(“Default") } } Output: A1. IO B1. IO A2. Default
  15. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    val local = ThreadLocal<String?>() launch(Dispatchers.IO) { local.set(“IO") println(“A1. ${local.get()}”) switchContext() println(“A2. ${local.get()}”) } suspend fun switchContext() { withContext(Dispatchers.Default) { println(“B1. ${local.get()}”) local.set(“Default") } } Output: A1. IO B1. null A2. Default
  16. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    @Synchronized fun critical() { println(“Start $thread”) Thread.sleep(100) println(“Stop $thread”) } repeat(2) { thread { critical() } } Output: Start worker-1 Stop worker-1 Start worker-3 Stop worker-3
  17. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    @Synchronized suspend fun critical() { println(“Start $thread”) delay(100) println(“Stop $thread”) } repeat(2) { launch(Dispatchers.Default) { critical() } } Output: Start worker-1 Start worker-3 Stop worker-1 Stop worker-3
  18. Lightweight Dispatchers Thread safety Threads? Coroutines !// on main thread

    suspend fun critical() { synchronized(obj) { println(“Start $thread”) delay(100) println(“Stop $thread”) } } repeat(2) { launch(Dispatchers.Default) { critical() } } Output: Start worker-1 Start worker-3 Stop worker-1 Stop worker-3 The 'delay' suspension point is inside a critical section
  19. Deferred Communicate Deferred is a non-blocking cancelable future
 val result:

    Deferred<Any> = async { fetchChannels() } println(“Deferred !-> ${result.await()}”)
  20. Deferred Communicate Deferred is a non-blocking cancelable future
 val result

    = CompletableDeferred<Json>() launch(Dispatchers.IO) { result.complete(slowFetch()) } println(“Deferred !-> ${result.await()}”)
  21. Deferred Mutex Communicate Mutex is Kotlins Mutual Exclusion
 val mutex

    = Mutex() suspend fun critical() { mutex.withLock { println(“Start $thread”) delay(100) println(“Stop $thread”) } } repeat(2) { launch(Dispatchers.Default) { critical() } }
  22. Deferred Mutex Channel Communicate Channels are synchronization primitives for streams


    suspend fun compete(name:String): String { delay(nextLong(5000)) return name } val race = Channel<String>() launch(…) { race.send(compete("Bob")) } launch(…) { race.send(compete(“Emilie")) } launch(…) { race.send(compete(“Charlie")) } launch(…) { race.send(compete(“Filippa")) } launch(Dispatchers.Default) { repeat(4) { println(“Name: ${race.receive()}") } race.close() }
  23. Deferred Mutex Channel Communicate Channels are synchronization primitives for streams


    val ch: Channel<Int> = produce { repeat(30) { send(it) } } repeat(3) { id !-> launch(Dispatchers.Default) { for(msg in ch) { println(“$id !-> $msg”) } } }
  24. Deferred Mutex Channel Communicate Channels are synchronization primitives for streams


    Channel<String>(7) !// BUFFERED Channel<String>(Channel.UNLIMITED) Channel<String>(Channel.CONFLATED) Channel<String>(Channel.RENDEZVOUS)
  25. Deferred Mutex Channel Flow Communicate Kotlins take on reactive streams


    val example = flow { for(i in 1!..10) { println(“Flow on !-> ${thread()}”) emit(i) } } !// on main thread example.filter { it !>= 5 } .map { it * 2 } .collect { println(“Collect on !-> ${thread()}”) }
  26. Deferred Mutex Channel Flow Communicate Kotlins take on reactive streams


    val example = flow { for(i in 1!..10) { println(“Flow on !-> ${thread()}”) emit(i) } }.flowOn(Dispatchers.Default) !// on main thread example.filter { it !>= 5 } .map { it * 2 } .collect { println(“Collect on !-> ${thread()}”) }
  27. Deferred Mutex Channel Flow Communicate Kotlins take on reactive streams


    val example = flow { for(i in 1!..10) { println(“Flow on !-> ${thread()}”) emit(i) } } !// on main thread example.filter { it !>= 5 } .map { it * 2 } .flowOn(Dispatchers.Default) .collect { println(“Collect on !-> ${thread()}”) }
  28. Deferred Mutex Channel Flow Communicate Kotlins take on reactive streams


    val example = flow { for(i in 1!..10) { println(“Flow on !-> ${thread()}”) emit(i) } } !// on main thread example.filter { it !>= 5 } .flowOn(Dispatchers.IO) .map { it * 2 } .flowOn(Dispatchers.Default) .collect { println(“Collect on !-> ${thread()}”) }