Robert Menke
08/22/2019, 2:42 PMChannel
, launch a coroutine in CoroutineContext
A to send some data through the channel, and then launch a coroutine in CoroutineContext
B in order to receive that data?
I threw together a basic example and it looks like you must send/receive to/from a channel in the same CoroutineContext
. If so, why is this the case?kingsley
08/22/2019, 2:49 PMAm I able toYes
it looks like you must send/receive to/from a channel in the sameNo. It doesn’t have to be the same coroutine context. You can go through the documentation on channels. It shows several examples of this https://kotlinlang.org/docs/reference/coroutines/channels.htmlCoroutineContext
Robert Menke
08/22/2019, 3:16 PMfun main() {
runBlocking {
produce()
consume()
}
}
val channel = Channel<String>()
suspend fun produce() {
coroutineScope {
launch {
println("Producing")
channel.send("Hello world")
println("Done producing")
}
}
}
suspend fun consume() {
coroutineScope {
launch {
println("Consuming")
val value = channel.receive()
println(value)
}
}
}
My understanding is that the coroutineScope
builder creates a new scope that inherits the context of its parent, which in this case is blocking.
I don’t understand why channel.send
is blocks the thread in this case though. Done producing
is never printed.fun main() {
runBlocking {
produce()
consume()
}
}
val channel = Channel<String>()
fun CoroutineScope.produce() {
launch {
println("Producing")
channel.send("Hello world")
println("Done producing")
}
}
fun CoroutineScope.consume() {
launch {
println("Consuming")
val value = channel.receive()
println(value)
}
}
fun main() {
runBlocking {
produce()
consume()
}
}
val channel = Channel<String>()
suspend fun produce() {
coroutineScope {
launch {
println("Producing")
channel.send("Hello world")
println("Done producing")
}
}
}
suspend fun consume() {
coroutineScope {
launch {
println("Consuming")
val value = channel.receive()
println(value)
}
}
}
but this does
fun main() {
runBlocking {
produce()
consume()
}
}
val channel = Channel<String>()
fun CoroutineScope.produce() {
launch {
println("Producing")
channel.send("Hello world")
println("Done producing")
}
}
fun CoroutineScope.consume() {
launch {
println("Consuming")
val value = channel.receive()
println(value)
}
}
??streetsofboston
08/22/2019, 3:44 PMcoroutineScope { ... }
only resumes/returns after all its child coroutines have finished.
In your example, the courtineScope
calls `launch`creating a child-coroutine that never finishes.
It never finishes, because this child-coroutine calls channeld.send
on a RENDEZVOUS channel. the send
call will never resume/return.channel.send
never resumes because the channel.receive
is never called.channel.receive
is never called because the consume
function is never called. This is because produce
never resumes/returns in your first code-sample.kingsley
08/22/2019, 3:45 PMproduce
and consume
are 2 suspending methods, and they both follow structured concurrency which is good.
But this means they will execute sequencially. consume
will only get called after produce
has completed. But this will never happen because produce will get suspended waiting for a consumer that will never come
The second example is indeed the right way to go about this, since each method is executed concurrently, so produce and consume are able to communicate with each other
If you really want 1 to work though, you could set a buffer value for the channel, but that doesn’t really scale if you want implement a proper channel based setupRobert Menke
08/22/2019, 3:47 PMBecausebeing the key reason 👍only resumes/returns after all its child coroutines have finished.coroutineScope { ... }