Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
elizarov@
Roman Elizarov
Kotlin Coroutines in
Practice
@relizarov
Coroutines recap
Coroutines are like
light-weight threads
suspend fun main() {
val jobs = List(100_000) {
GlobalScope.launch {
delay(5000)
print(".")
}
}
jobs.forEach { it.join() }...
suspend fun main() {
val jobs = List(100_000) {
GlobalScope.launch {
delay(5000)
print(".")
}
}
jobs.forEach { it.join() }...
suspend fun main() {
val jobs = List(100_000) {
GlobalScope.launch {
delay(5000)
print(".")
}
}
jobs.forEach { it.join() }...
Quantity
Quantity → Quality
A practical challenge
suspend fun downloadContent(location: Location): Content
fun processReferences(refs: List<Reference>)
References
References
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
…
}
}
References Locations
resolve
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolve...
References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val loca...
References Locations Contents
resolve download
suspend fun processReferences(refs: List<Reference>) {
for (ref in refs) {
...
References Locations Contents
resolve download
suspend fun processReferences(refs: List<Reference>) {
for (ref in refs) {
...
References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val loca...
References Locations Contents
resolve download
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val loca...
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch...
fun processReferences(refs: List<Reference>) {
for (ref in refs) {
val location = ref.resolveLocation()
GlobalScope.launch...
ref1 location1 Launch download
ref2 location2 Launch download
ref3
Crash!
Crash! Leak!
fun processReferences(refs: List<Re...
Structured
concurrency
fun processReferences(refs: List<Reference>)
suspend fun processReferences(refs: List<Reference>)
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
…
}
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
Crash?
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.reso...
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
Crash?
cancels
Waits for completion
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in re...
suspend fun processReferences(refs: List<Reference>) =
coroutineScope {
for (ref in refs) {
val location = ref.resolveLoca...
The state
References Contents
Download process
Reference 1
Content
Reference 2
Location
Download process
State
class Downloader {
}
class Downloader {
private val requested = mutableSetOf<Location>()
}
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
class Downloader {
private val requested = mutableSetOf<Location>()
fun downloadReference(ref: Reference) {
val location =...
Shared
Mutable State
Share by
Communicating
Synchronization
Primitives
Communication
Primitives
classes coroutines
launch {
val requested = mutableSetOf<Location>()
…
}
Does not share mutable state
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (reque...
launch {
val requested = mutableSetOf<Location>()
for (ref in references) {
val location = ref.resolveLocation()
if (reque...
Channel
References
Downloader
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
…
}
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
…
}
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
…
}
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
…
}
Convention
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
) =
launch {
val requested = mutableSetOf<Location>(...
Limiting
concurrency
Worker 1
Worker 2
Worker 3
Worker N
Worker pool
…
References
Downloader
references locations
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
locations: SendChannel<Location>
)
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
locations: SendChannel<Location>
) =
launch {
val re...
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
)
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
)
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
) =
launch {
for (loc in locations) {
val content = downloa...
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
) =
launch {
for (loc in locations) {
val content = downloa...
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
) =
launch {
for (loc in locations) {
val content = downloa...
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
) =
launch {
for (loc in locations) {
val content = downloa...
fun CoroutineScope.worker(
locations: ReceiveChannel<Location>
) =
launch {
for (loc in locations) {
val content = downloa...
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
Refs ↔ Locs
location &
content
data class LocContent(val loc: Location, val content: Content)
data class LocContent(val loc: Location, val content: Content)
fun CoroutineScope.worker(
locations: ReceiveChannel<Locati...
data class LocContent(val loc: Location, val content: Content)
fun CoroutineScope.worker(
locations: ReceiveChannel<Locati...
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
locations: SendChannel<Location>,
contents: ReceiveC...
fun CoroutineScope.downloader(
references: ReceiveChannel<Reference>,
locations: SendChannel<Location>,
contents: ReceiveC...
Select
select {
references.onReceive { ref ->
…
}
contents.onReceive { (loc, content) ->
…
}
}
select {
references.onReceive { ref ->
…
}
contents.onReceive { (loc, content) ->
…
}
}
select<Unit> {
references.onReceive { ref ->
…
}
contents.onReceive { (loc, content) ->
…
}
}
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
…
}
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
launch {
val requested = mutableMapOf<Location, MutableList<Reference>>()
while (true) {
select<Unit> {
references.onRecei...
Putting it all
together
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
references
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
references
fun CoroutineScope.processReferences(
references: ReceiveChannel<Reference>
)
fun CoroutineScope.processReferences(
references: ReceiveChannel<Reference>
)
fun CoroutineScope.processReferences(
references: ReceiveChannel<Reference>
) {
val locations = Channel<Location>()
val co...
fun CoroutineScope.processReferences(
references: ReceiveChannel<Reference>
) {
val locations = Channel<Location>()
val co...
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
references
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
references
processReferences
Worker 1
Worker 2
Worker 3
Worker N
…
References
Downloader
locations
contents
references
processReferences
Patterns
every...
fun CoroutineScope.processReferences(…)
Root
CoroutineScope
class SomethingWithLifecycle {
}
class SomethingWithLifecycle : CoroutineScope {
}
class SomethingWithLifecycle : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = …
}
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
ge...
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
fun dispose() { … }
override val coroutineContext:...
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
fun close() { … }
override val coroutineContext: C...
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
fun close() {
job.cancel()
}
override val coroutin...
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
fun close() {
job.cancel()
}
override val coroutin...
class SomethingWithLifecycle : CoroutineScope {
private val job = Job()
fun close() {
job.cancel()
}
override val coroutin...
class SomethingWithLifecycle : CoroutineScope {
…
override val coroutineContext: CoroutineContext
get() = job + Dispatcher...
class SomethingWithLifecycle : CoroutineScope {
…
override val coroutineContext: CoroutineContext
get() = job + Dispatcher...
class SomethingWithLifecycle : CoroutineScope {
…
override val coroutineContext: CoroutineContext
get() = job + Dispatcher...
suspend vs scope
suspend fun downloadContent(location: Location): Content
suspend fun downloadContent(location: Location): Content
Does something long &
waits for it to complete without blocking
suspend fun downloadContent(location: Location): Content
fun CoroutineScope.processReferences(…)
suspend fun downloadContent(location: Location): Content
fun CoroutineScope.processReferences(…)
Launches new coroutines &...
Takeaway
Coroutines are like
light-weight threads
Coroutines are NOT like
threads
Coroutines are NOT like
threads
Rethink the way you
structure your code
Thank you
Any questions?
elizarov@
Roman Elizarov
@relizarov
Nächste SlideShare
Wird geladen in …5
×

Kotlin Coroutines in Practice @ KotlinConf 2018

1.902 Aufrufe

Veröffentlicht am

Kotlin Coroutines in Practice, presented at KotlinConf 2018, video is available here https://www.youtube.com/watch?v=a3agLJQ6vt8

Veröffentlicht in: Technologie
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE Format, ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Antworten 
    Sind Sie sicher, dass Sie …  Ja  Nein
    Ihre Nachricht erscheint hier
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE Format, ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Antworten 
    Sind Sie sicher, dass Sie …  Ja  Nein
    Ihre Nachricht erscheint hier

Kotlin Coroutines in Practice @ KotlinConf 2018

  1. 1. elizarov@ Roman Elizarov Kotlin Coroutines in Practice @relizarov
  2. 2. Coroutines recap
  3. 3. Coroutines are like light-weight threads
  4. 4. suspend fun main() { val jobs = List(100_000) { GlobalScope.launch { delay(5000) print(".") } } jobs.forEach { it.join() } } Coroutines are like light-weight threads
  5. 5. suspend fun main() { val jobs = List(100_000) { GlobalScope.launch { delay(5000) print(".") } } jobs.forEach { it.join() } } Coroutines are like light-weight threads
  6. 6. suspend fun main() { val jobs = List(100_000) { GlobalScope.launch { delay(5000) print(".") } } jobs.forEach { it.join() } } Coroutines are like light-weight threads
  7. 7. Quantity
  8. 8. Quantity → Quality
  9. 9. A practical challenge suspend fun downloadContent(location: Location): Content
  10. 10. fun processReferences(refs: List<Reference>) References
  11. 11. References fun processReferences(refs: List<Reference>) { for (ref in refs) { … } }
  12. 12. References Locations resolve fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() … } }
  13. 13. References Locations Contents resolve download fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() val content = downloadContent(location) processContent(ref, content) } }
  14. 14. References Locations Contents resolve download suspend fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() val content = downloadContent(location) processContent(ref, content) } }
  15. 15. References Locations Contents resolve download suspend fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() val content = downloadContent(location) processContent(ref, content) } } Sequential
  16. 16. References Locations Contents resolve download fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() val content = downloadContent(location) processContent(ref, content) } } Parallel
  17. 17. References Locations Contents resolve download fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() GlobalScope.launch { val content = downloadContent(location) processContent(ref, content) } } } Parallel
  18. 18. fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() GlobalScope.launch { val content = downloadContent(location) processContent(ref, content) } } } Coroutines are cheap! What could go wrong?
  19. 19. fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() GlobalScope.launch { val content = downloadContent(location) processContent(ref, content) } } } ref1 location1 Launch download ref2 location2 Launch download ref3 Crash! Crash!
  20. 20. ref1 location1 Launch download ref2 location2 Launch download ref3 Crash! Crash! Leak! fun processReferences(refs: List<Reference>) { for (ref in refs) { val location = ref.resolveLocation() GlobalScope.launch { val content = downloadContent(location) processContent(ref, content) } } }
  21. 21. Structured concurrency
  22. 22. fun processReferences(refs: List<Reference>)
  23. 23. suspend fun processReferences(refs: List<Reference>)
  24. 24. suspend fun processReferences(refs: List<Reference>) = coroutineScope { … }
  25. 25. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() GlobalScope.launch { val content = downloadContent(location) processContent(ref, content) } } }
  26. 26. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } }
  27. 27. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } } Child
  28. 28. Crash? suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } }
  29. 29. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } } Crash?
  30. 30. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } } Crash? cancels
  31. 31. Crash? cancels Waits for completion suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } }
  32. 32. suspend fun processReferences(refs: List<Reference>) = coroutineScope { for (ref in refs) { val location = ref.resolveLocation() launch { val content = downloadContent(location) processContent(ref, content) } } } Never leaks jobs
  33. 33. The state
  34. 34. References Contents Download process
  35. 35. Reference 1 Content Reference 2 Location Download process State
  36. 36. class Downloader { }
  37. 37. class Downloader { private val requested = mutableSetOf<Location>() }
  38. 38. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() … } }
  39. 39. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } } }
  40. 40. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  41. 41. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } } Concurrent
  42. 42. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } } Concurrent
  43. 43. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } } Shared mutable state
  44. 44. class Downloader { private val requested = mutableSetOf<Location>() fun downloadReference(ref: Reference) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } } Shared mutable state Needs Synchronization Shared + Mutable =
  45. 45. Shared Mutable State Share by Communicating
  46. 46. Synchronization Primitives Communication Primitives
  47. 47. classes coroutines
  48. 48. launch { val requested = mutableSetOf<Location>() … } Does not share mutable state
  49. 49. launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  50. 50. launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  51. 51. Channel References Downloader
  52. 52. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { … }
  53. 53. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { … }
  54. 54. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { … }
  55. 55. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { … } Convention
  56. 56. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  57. 57. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  58. 58. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  59. 59. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { // schedule download } // ... wait for result ... processContent(ref, content) } }
  60. 60. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { launch { … } } // ... wait for result ... processContent(ref, content) } } Coroutines are cheap! What could go wrong?
  61. 61. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { launch { … } } // ... wait for result ... processContent(ref, content) } } Child
  62. 62. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { launch { … } } // ... wait for result ... processContent(ref, content) } } Coroutines are cheap! But the work they do…
  63. 63. Limiting concurrency
  64. 64. Worker 1 Worker 2 Worker 3 Worker N Worker pool … References Downloader references locations
  65. 65. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, locations: SendChannel<Location> )
  66. 66. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, locations: SendChannel<Location> ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { locations.send(location) } } }
  67. 67. fun CoroutineScope.worker( locations: ReceiveChannel<Location> )
  68. 68. fun CoroutineScope.worker( locations: ReceiveChannel<Location> )
  69. 69. fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent(loc) processContent(ref, content) } }
  70. 70. fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent(loc) processContent(ref, content) } } Fan-out
  71. 71. fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent(location) processContent(ref, content) } }
  72. 72. fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent(loc) processContent(ref, content) } }
  73. 73. fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent(loc) processContent(ref, content) } }
  74. 74. Worker 1 Worker 2 Worker 3 Worker N … References Downloader
  75. 75. Worker 1 Worker 2 Worker 3 Worker N … References Downloader Refs ↔ Locs location & content
  76. 76. data class LocContent(val loc: Location, val content: Content)
  77. 77. data class LocContent(val loc: Location, val content: Content) fun CoroutineScope.worker( locations: ReceiveChannel<Location>, contents: SendChannel<LocContent> )
  78. 78. data class LocContent(val loc: Location, val content: Content) fun CoroutineScope.worker( locations: ReceiveChannel<Location>, contents: SendChannel<LocContent> ) = launch { for (loc in locations) { val content = downloadContent(loc) contents.send(LocContent(loc, content)) } }
  79. 79. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents
  80. 80. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, locations: SendChannel<Location>, contents: ReceiveChannel<LocContent> )
  81. 81. fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, locations: SendChannel<Location>, contents: ReceiveChannel<LocContent> ) = launch { val requested = mutableSetOf<Location>() for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { locations.send(location) } } } Hmm….
  82. 82. Select
  83. 83. select { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> … } }
  84. 84. select { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> … } }
  85. 85. select<Unit> { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> … } }
  86. 86. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() … }
  87. 87. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> … } } } }
  88. 88. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> … } } } }
  89. 89. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> val loc = ref.resolveLocation() … } contents.onReceive { (loc, content) -> … } } } }
  90. 90. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> val loc = ref.resolveLocation() val refs = requested[loc] … } contents.onReceive { (loc, content) -> … } } } }
  91. 91. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> val loc = ref.resolveLocation() val refs = requested[loc] if (refs == null) { requested[loc] = mutableListOf(ref) locations.send(loc) } } contents.onReceive { (loc, content) -> … } } } }
  92. 92. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> val loc = ref.resolveLocation() val refs = requested[loc] if (refs == null) { requested[loc] = mutableListOf(ref) locations.send(loc) } else { refs.add(ref) } } contents.onReceive { (loc, content) -> … } } } }
  93. 93. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> val loc = ref.resolveLocation() val refs = requested[loc] if (refs == null) { requested[loc] = mutableListOf(ref) locations.send(loc) } else { refs.add(ref) } } contents.onReceive { (loc, content) -> … } } } } No concurrency No synchronization
  94. 94. launch { val requested = mutableMapOf<Location, MutableList<Reference>>() while (true) { select<Unit> { references.onReceive { ref -> … } contents.onReceive { (loc, content) -> val refs = requested.remove(loc)!! for (ref in refs) { processContent(ref, content) } } } } }
  95. 95. Putting it all together
  96. 96. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents references
  97. 97. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents references
  98. 98. fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> )
  99. 99. fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> )
  100. 100. fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> ) { val locations = Channel<Location>() val contents = Channel<LocContent>() repeat(N_WORKERS) { worker(locations, contents) } downloader(references, locations, contents) }
  101. 101. fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> ) { val locations = Channel<Location>() val contents = Channel<LocContent>() repeat(N_WORKERS) { worker(locations, contents) } downloader(references, locations, contents) }
  102. 102. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents references
  103. 103. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents references processReferences
  104. 104. Worker 1 Worker 2 Worker 3 Worker N … References Downloader locations contents references processReferences Patterns everywhere Worker pool Actor
  105. 105. fun CoroutineScope.processReferences(…)
  106. 106. Root CoroutineScope
  107. 107. class SomethingWithLifecycle { }
  108. 108. class SomethingWithLifecycle : CoroutineScope { }
  109. 109. class SomethingWithLifecycle : CoroutineScope { override val coroutineContext: CoroutineContext get() = … }
  110. 110. class SomethingWithLifecycle : CoroutineScope { private val job = Job() override val coroutineContext: CoroutineContext get() = … }
  111. 111. class SomethingWithLifecycle : CoroutineScope { private val job = Job() fun dispose() { … } override val coroutineContext: CoroutineContext get() = … }
  112. 112. class SomethingWithLifecycle : CoroutineScope { private val job = Job() fun close() { … } override val coroutineContext: CoroutineContext get() = … }
  113. 113. class SomethingWithLifecycle : CoroutineScope { private val job = Job() fun close() { job.cancel() } override val coroutineContext: CoroutineContext get() = … }
  114. 114. class SomethingWithLifecycle : CoroutineScope { private val job = Job() fun close() { job.cancel() } override val coroutineContext: CoroutineContext get() = job }
  115. 115. class SomethingWithLifecycle : CoroutineScope { private val job = Job() fun close() { job.cancel() } override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main }
  116. 116. class SomethingWithLifecycle : CoroutineScope { … override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main fun doSomething() { } }
  117. 117. class SomethingWithLifecycle : CoroutineScope { … override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main fun doSomething() { launch { … } } }
  118. 118. class SomethingWithLifecycle : CoroutineScope { … override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main fun doSomething() { processReferences(references) } } Never leak any coroutines
  119. 119. suspend vs scope
  120. 120. suspend fun downloadContent(location: Location): Content
  121. 121. suspend fun downloadContent(location: Location): Content Does something long & waits for it to complete without blocking
  122. 122. suspend fun downloadContent(location: Location): Content fun CoroutineScope.processReferences(…)
  123. 123. suspend fun downloadContent(location: Location): Content fun CoroutineScope.processReferences(…) Launches new coroutines & quickly returns, does not wait for them
  124. 124. Takeaway
  125. 125. Coroutines are like light-weight threads
  126. 126. Coroutines are NOT like threads
  127. 127. Coroutines are NOT like threads Rethink the way you structure your code
  128. 128. Thank you Any questions? elizarov@ Roman Elizarov @relizarov

×