我有一个使用协程渠道的简单生产者和消费者。这是一个愚蠢的版本:
class Producer {
suspend fun start(): ReceiveChannel<String> {
val channel = Channel<String>(Channel.UNLIMITED)
// Asynchronous channel.send(it) from an object callback
channel.invokeOnClose {
// Channel is closed...
}
return channel
}
}
class Consumer : CoroutineScope {
private val producer = Producer()
private val job = Job()
override val coroutineContext = job + Dispatchers.Default
fun start() {
launch {
val channel = producer.start()
for (currentValue in channel) {
// use currentValue
}
}
}
fun stop() {
job.cancel()
}
}
Producer
创建一个通道,然后用异步作业中的值填充它。 Consumer
对其进行迭代并使用这些值。
我的期望是,当我从使用者调用job.cancel()
时,通道迭代器将抛出并且通道将关闭。永远不会调用invokeOnClose
回调。
我可以维护对Consumer
中的频道的引用,并执行channel.close()
。我想知道是否有更聪明的解决方案。也许是迭代频道值的另一种方法?谢谢吗?
修改
好像使用
launch {
val channel = producer.start()
channel.consumeEach { currentValue ->
// use currentValue
}
}
可以解决问题。但是consumeEach()
被标记为过时。
答案 0 :(得分:0)
您期望job.cancel()
会传播到您的生产者,但是Producer
实际上与任何东西都不相关。将函数标记为suspend
并不能使其成为协程。
这是一种通过结构化并发解决此问题的方法:
class Producer: CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Job() + Dispatchers.Default
suspend fun start() = produce<String> {
channel.send("A")
channel.invokeOnClose {
println("Closed")
}
}
}
现在您的Producer
知道CoroutineScope
。
由于我们使用的是produce
,因此您无需像以前那样初始化频道。