Channel
Channel是协程间的管道通信方式,数据之间只能支持一对一的传输关系,如果想要实现一对多的关系,请使用broadcast。
Channel的构建:
public fun <E> Channel(
capacity: Int = RENDEZVOUS, // 数据容量大小
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, // 协程数据超过capacity后的状态
onUndeliveredElement: ((E) -> Unit)? = null // 没有接收者的回调
)
Channel在创建时可以指定他的capacity(数据容量)模式,它的模式见下:
模式 | 取值 | 描述 |
---|---|---|
UNLIMITED | Int.MAX_VALUE | 无限容量的管道 |
RENDEZVOUS(默认) | 0 | 无容量的管道。 |
CONFLATED | -1 | 保留最新,receive只能获得最近一次send的值 |
BUFFERED | -2 | 默认容量,可通过程序参数设置默认大小,默认为64。当管道中的数据量达到64时挂起。 |
”FIXED“(固定值) | 1~x | 固定容量,通过参数执行缓存大小,当管道中的数据量达到缓存值时挂起。 |
协程数据超过capacity后发送者状态由BufferOverflow来决定:
模式 | 取值 | 描述 |
---|---|---|
SUSPEND | 协程直接挂起 | 无限容量,send调用后直接返回。 |
DROP_OLDEST | 丢弃掉最久的数据 | 不见不散,send调用后挂起直到receive到达。 |
DROP_LATEST | 丢弃掉最新的数据 | 保留最新,receive只能获得最近一次send的值 |
onUndeliveredElement当数据没有接收者接收时会被动性的调用
注:
-
CONFLATED模式下只能设置onBufferOverflow == BufferOverflow.SUSPEND
-
CONFLATED模式等价于Channel<E>(Channel.RENDEZVOUS,BufferOverflow.DROP_LATEST)
使用方法:
val channel_unlimited = Channel<Int>(Channel.UNLIMITED)
val channel_rendezvous = Channel<Int>(Channel.RENDEZVOUS,BufferOverflow.DROP_LATEST)
val channel_conflated = Channel<Int>(
Channel.CONFLATED,
BufferOverflow.SUSPEND
) { println("onUndeliveredElement i=${it}") }
val channel_buffered = Channel<Int>(Channel.BUFFERED)
val channel_fixed = Channel<Int>(3)
var channel_select = channel_rendezvous
val produce = GlobalScope.launch {
for(i in 0..10){
channel_select.send(i)
}
}
val consumer = GlobalScope.launch {
while (!channel_select.isClosedForReceive){
val value = channel_select.receiveOrNull()
println(value)
}
}
Build模式:
Coroutines中提供了两种简单的构建者模式(produce和actor)来帮助我们快速的构建Channel。
模式 | 描述 |
---|---|
produce | 启动一个生产者协程,返回ReceiveChannel |
actor | 启动一个消费者卸载,返回SendChannel |
示例:
suspend fun producer(){
val receiveChannel = GlobalScope.produce<Int>(capacity = Channel.UNLIMITED) {
for(i in 0..3){
send(i)
}
}
}
suspend fun consumer(){
val sendChannel = GlobalScope.actor<Int>(capacity = Channel.UNLIMITED) {
for(i in 0..3){
println(receive())
}
}
}
broadcast(广播):
当发送者与接收者的关系为一对多时适用,广播的数据会发送给所有的接收者。
注:广播由于不需要等待接收方响应,所以不可使用RENDEZVOUS模式
示例:
suspend fun broadcast(){
val broadcastChannel = GlobalScope.broadcast<Int>(capacity = Channel.UNLIMITED) {
for(i in 0..3){
send(i)
}
}
List(5){
GlobalScope.launch {
val receiveChannel = broadcastChannel.openSubscription()
for(i in receiveChannel){
println(i)
}
}
}
}