创建 & 容量/溢出
ini
val ch = Channel<T>(
capacity = Channel.BUFFERED, // 0(RENDEZVOUS) | BUFFERED(~64) | UNLIMITED | CONFLATED | N
onBufferOverflow = BufferOverflow.SUSPEND // SUSPEND | DROP_OLDEST | DROP_LATEST
)
- RENDEZVOUS(0) :无缓冲,强背压;BUFFERED :小缓冲常用;UNLIMITED :慎用,易 OOM;CONFLATED:只保最新。
- 选型:必达→SUSPEND ;只要最新→CONFLATED / DROP_OLDEST ;滑窗→N+DROP_OLDEST。
发送 / 接收
kotlin
suspend fun SendChannel<T>.send(x) // 可能挂起(形成背压)
fun SendChannel<T>.trySend(x) // 非挂起,失败立即返回
suspend fun ReceiveChannel<T>.receive() // 队列空会挂起
fun ReceiveChannel<T>.tryReceive() // 非挂起
for (e in ch) { /* 读到 close 且队列清空时自然结束 */ }
小技巧:先非阻塞、失败再挂起
kotlin
suspend fun <T> SendChannel<T>.offerOrSend(x: T) {
if (!trySend(x).isSuccess) send(x)
}
关闭 / 取消
- close(cause?):不再接收新元素;已入队会被读完后结束(优雅收尾)。
- cancel(cause?):立即终止,可能丢未读(紧急停机)。
- 接收安全模板:
kotlin
while (true) when (val r = ch.receiveCatching()) {
is ChannelResult.Success -> handle(r.getOrNull()!!)
is ChannelResult.Closed -> break
}
常用并发模式
- pipeline:多段 Channel 串联;每段容量=节流阀;重段可开 N 个 worker。
- fan-in :多生产者→单通道;由协调者统一 close。
- fan-out :单生产者→多消费者(单播/竞争消费);要广播请用 SharedFlow。
- actor:把共享状态封进单协程(邮箱 Channel),串行处理,免锁。
与 Flow / SharedFlow 互通
-
Channel → Flow:receiveAsFlow()(保留通道) / consumeAsFlow()(收完就 cancel)。
-
Flow → Channel:produceIn(scope) 或 .onEach { ch.send(it) }.launchIn(scope)。
-
Flow → SharedFlow/StateFlow:shareIn(...) / stateIn(...)。
-
SharedFlow ↔ Channel:shared.produceIn(scope)、launch { for (e in ch) shared.emit(e) }。
取舍:强背压 & 单播 → Channel ;多播 & 状态(最新值)→ SharedFlow/StateFlow ;拉式流水线/按需计算 → Flow。
时间与选择器(速率/超时/心跳)
scss
withTimeoutOrNull(300) { ch.receive() } // 接收超时→null
withTimeout(200) { ch.send(cmd) } // 发送限时(SUSPEND 满会超时)
val tick = ticker(200, 0) // 周期节拍器(ReceiveChannel<Unit>)
select { // 谁先就选谁
ch.onReceive { process(it) }
tick.onReceive { flush() }
onTimeout(3000) { markStale() }
}
调优与守则(超精简)
- 容量从 64 起步:过大→高延迟;过小→抖动。
- 高频但可丢中间:DROP_OLDEST / CONFLATED ;必须逐条:SUSPEND/RENDEZVOUS。
- 给持资源元素配 onUndeliveredElement 做释放。
- 正常收尾:生产者 close → 消费者读干净 ;紧急:cancel。
- 别用 UNLIMITED 掩盖压力;监控队列长度(自增自减计数打点)。
迷你示例:UI 只要最新 + 限频
scss
val latest = Channel<State>(Channel.CONFLATED)
launch(Dispatchers.Default) {
while (isActive) latest.trySend(snapshot())
}
launch(Dispatchers.Main) {
latest.receiveAsFlow().sample(200).collect(::render)
}