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) } }
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) } }
f un Cor out i neScope. downl oader ( r ef er ences: Recei veChannel <Ref er ence>, ) = launch { r equest ed = mutableSetOf <Locat i on>( ) val f or ( r ef i n r ef er ences) { l ocat i on = r ef . r esol veLocat i on( ) val i f ( r equest ed. add( l ocat i on) ) { // schedule download } // ... wait for result ... processContent ( r ef , cont ent ) } }
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?
fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = launch { val requested = mutableSetOf <Location>() for (ref in references) { Child val location = ref.resolveLocation() if (requested.add(location)) { launch { … } } // ... wait for result ... processContent (ref, content) } }
fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, ) = l aunch { val requested = m f <Location>() ut abl eSet O for (ref in references) { val location = ref.resolveLocation() if (requested.add(location)) { l aunch { … } } / / . . . wai t f or r esul t . . . pr ocessCont ent (ref, content) } } Coroutines are cheap! But the work they do…
Limiting concurrency
Worker pool Worker 1 Worker 2 references locations References Worker 3 … Downloader Worker N
fun Cor out i neScope. downl oader ( r ef er ences: Recei veChannel <Ref er ence>, l ocat i ons: SendChannel <Locat i on> )
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) } } }
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 = downloadContent (loc) processContent (ref, content) } }
f un CoroutineScope.worker( Fan-out locations: ReceiveChannel<Location> ) = launch { f or (loc i n locations) { content = downloadContent (loc) val processContent (ref, content) } }
fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent (location) processContent (ref, content) } }
fun CoroutineScope.worker( locations: ReceiveChannel<Location> ) = launch { for (loc in locations) { val content = downloadContent (loc) processContent (ref, content) } }
fun Cor out i neScope. wor ker ( l ocat i ons: Recei veChannel <Locat i on> ) = launch { for ( l oc in l ocat i ons) { val cont ent = downloadContent ( l oc) processContent ( r ef , cont ent ) } }
Worker 1 Worker 2 References Worker 3 … Downloader Worker N
Ref s ↔ Locs Worker 1 Worker 2 Ref er ences Worker 3 … Downl oader l ocat i on & cont ent Worker N
data class LocContent( val loc : Location, val content : Content)
data class LocContent( val loc : Location, val content : Content) fun CoroutineScope.worker( locations: ReceiveChannel<Location>, contents: SendChannel<LocContent> )
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)) } }
Worker 1 locations Worker 2 References Worker 3 … Downloader contents Worker N
fun CoroutineScope.downloader( references: ReceiveChannel<Reference>, locations: SendChannel<Location>, contents: ReceiveChannel<LocContent> )
fun Cor out i neScope. downl oader ( r ef er ences: Recei veChannel <Ref er ence>, l ocat i ons: SendChannel <Locat i on>, Hmm…. cont ent s: Recei veChannel <LocCont ent > ) = launch { val r equest ed = mutableSetOf <Locat i on>( ) for ( r ef in r ef er ences) { val l ocat i on = r ef . r esol veLocat i on( ) if ( r equest ed. add( l ocat i on) ) { l ocat i ons. send( l ocat i on) } } }
Select
sel ect { r ef er ences. onRecei ve { r ef - > … } cont ent s. onRecei ve { ( l oc, cont ent ) - > … } }
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 r equest ed = mutableMapOf <Locat i on, M ut abl eLi st <Ref er ence>>( ) while ( true ) { sel ect <Uni t > { r ef er ences. onReceive { r ef -> … } cont ent s. onReceive { ( l oc, cont ent ) -> … } } } }
launch { val r equest ed = mutableMapOf <Locat i on, M ut abl eLi st <Ref er ence>>( ) while ( true ) { sel ect <Uni t > { r ef er ences. onReceive { r ef -> … } cont ent s. onReceive { ( l oc, cont ent ) -> … } } } }
launch { val requested = mutableMapOf <Location, MutableList<Reference>>() while ( true ) { select<Unit> { references. onReceive { ref -> val loc = ref.resolveLocation() … } contents. onReceive { (loc, content) -> … } } } }
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) -> … } } } }
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) -> … } } } }
l aunch { val requested = m f <Location, MutableList<Reference>>() ut abl eM apO while ( true ) { select<Unit> { references. onReceive { ref -> val loc = ref.resolveLocation() val refs = requested[loc] if (refs != null ) { requested[loc] = m f (ref) ut abl eLi st O locations.send(loc) } else { refs.add(ref) } } contents. onReceive { (loc, content) -> … } } } }
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 ) { No concurrency requested[loc] = mutableListOf (ref) locations.send(loc) No synchronization } else { refs.add(ref) } } contents. onReceive { (loc, content) -> … } } } }
launch { val r equest ed = mutableMapOf <Locat i on, M ut abl eLi st <Ref er ence>>( ) while ( true ) { sel ect <Uni t > { r ef er ences. onReceive { r ef -> … } cont ent s. onReceive { ( l oc, cont ent ) -> val r ef s = r equest ed. r em ove( l oc) ! ! for ( r ef in r ef s) { processContent ( r ef , cont ent ) } } } } }
Putting it all together
Worker 1 locations Worker 2 References Worker 3 references … Downloader contents Worker N
Worker 1 locations Worker 2 References Worker 3 references … Downloader contents Worker N
fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> )
fun Cor out i neScope. pr ocessRef er ences( r ef er ences: Recei veChannel <Ref er ence> )
fun CoroutineScope.processReferences( references: ReceiveChannel<Reference> ) { val locations = Channel <Location>() val contents = Channel <LocContent>() repeat ( N_WORKERS ) { worker (locations, contents) } downloader (references, locations, contents) }
Recommend
More recommend