Kotlin Channel 开箱即用

创建 & 容量/溢出

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)
}
相关推荐
小的~~4 小时前
面试被问懵了?为什么 Redis 单线程还能保证 Lua 脚本原子性,却偏偏选了 Lua?
redis·面试·lua
程序员爱钓鱼7 小时前
Go 编程实战:函数 Function——参数、返回值与代码复用
后端·面试·go
艾莉丝努力练剑7 小时前
【AI接入大模型SDK】ChatSDK示例验证
人工智能·学习·面试·大模型·sdk
码匠许师傅16 小时前
【C++ 面试真题】24. 聊聊 C++ 的时间日期处理
java·c++·面试
Patrick_Wilson18 小时前
Web 认证方案技术指南
前端·后端·面试
神奇的程序员19 小时前
在这个独属于ai的时代,我终究是被裁员了
前端·后端·面试
触底反弹21 小时前
🔥 从「为什么」到「怎么用」:TypeScript 类型约束与泛型完全指南
后端·面试·typescript
李剑一21 小时前
有点干,前端架构基础之:Web Worker到底是什么?它和Java中的线程是一个道理吗?
前端·面试·架构
leo_yu_yty1 天前
Mysql 面试准备
数据库·mysql·面试
六边形6661 天前
独立开发不知道做什么?使用 TRAE Work 抓取差评痛点,快速跑通产品立项流
前端·后端·面试