朱涛 · Kotlin 编程第一课
朱涛
Google 认证的 Kotlin、Android 开发者专家,博客“Kotlin Jetpack 实战”作者
6717 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 50 讲
朱涛 · Kotlin 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

19 | Channel:为什么说Channel是“热”的?

你好,我是朱涛。
前面我们学习的挂起函数、async,它们一次都只能返回一个结果。但在某些业务场景下,我们往往需要协程返回多个结果,比如微信等软件的 IM 通道接收的消息,或者是手机 GPS 定位返回的经纬度坐标需要实时更新。那么,在这些场景下,我们之前学习的协程知识就无法直接解决了。
而今天我要讲解的 Kotlin 协程中的 Channel,就是专门用来做这种事情的。类似的需求,如果我们不使用 Channel 而是用其他的并发手段配合集合来做的话,其实也能实现,但复杂度会大大增加。那么接下来,我们就一起来学习下 Channel。

Channel 就是管道

顾名思义,Channel 就是一个管道。我们可以用这个概念,先来建立一个思维模型:
Channel 这个管道的其中一端,是发送方;管道的另一端是接收方。而管道本身,则可以用来传输数据。
所以,我们根据上面的思维模型,很容易就能写出下面的代码。
// 代码段1
fun main() = runBlocking {
// 1,创建管道
val channel = Channel<Int>()
launch {
// 2,在一个单独的协程当中发送管道消息
(1..3).forEach {
channel.send(it) // 挂起函数
logX("Send: $it")
}
}
launch {
// 3,在一个单独的协程当中接收管道消息
for (i in channel) { // 挂起函数
logX("Receive: $i")
}
}
logX("end")
}
/*
================================
end
Thread:main @coroutine#1
================================
================================
Receive: 1
Thread:main @coroutine#3
================================
================================
Send: 1
Thread:main @coroutine#2
================================
================================
Send: 2
Thread:main @coroutine#2
================================
================================
Receive: 2
Thread:main @coroutine#3
================================
================================
Receive: 3
Thread:main @coroutine#3
================================
================================
Send: 3
Thread:main @coroutine#2
================================
// 4,程序不会退出
*/
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Kotlin协程中的Channel技术是一项重要的并发处理工具,用于解决协程需要返回多个结果的场景。相比传统的挂起函数和async,Channel可以实现实时更新的消息接收和数据传输,大大简化了复杂度。本文深入探讨了Channel的应用场景、与其他并发手段的比较,以及其“热”特性。通过学习Channel,读者可以更好地理解Kotlin协程的特性和应用,为解决实际业务问题提供了新的思路和工具。 文章首先介绍了Channel的基本概念和创建方式,强调了其在传递多个数据组成的流时的重要性。同时,文章还探讨了Channel的“热”特性,指出无论是否有接收方,Channel的发送方都会主动工作,为数据流传递提供了便利。此外,文章还分析了Channel的源码定义,指出其能力是来自于SendChannel、ReceiveChannel这两个接口的组合,为读者提供了对Channel设计的更深入理解。 总的来说,本文通过深入讨论Channel的特性和使用方式,为读者提供了对Kotlin协程中Channel技术的全面了解,为他们在实际业务中应用Kotlin协程提供了有益的指导和建议。通过本文的阅读,读者可以快速了解Channel技术的重要性和应用价值,为其在并发处理中的实际应用提供了有力支持。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《朱涛 · Kotlin 编程第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(21)

  • 最新
  • 精选
  • Allen
    Channel 是“热”的可能会导致一下几个问题: 1. 可能会导致数据的丢失。 2. 浪费不必要的程序资源,类似于非懒加载的情况。 3. 如果未及时 close 的话,可能会导致内存泄露。

    作者回复: 总结到位~

    2022-03-02
    14
  • jim
    Channel平时工作中有 哪些使用场景???

    作者回复: 常规业务开发其实很少会需要用到Channel,Channel的使用场景其实是比较偏底层的,比如说我在课程开头提到的IM消息通道、股票行情实时刷新,等等。

    2022-03-31
    4
  • 神佑小鹿
    1、最开始 channel.receive () 先调用,但是 channel 没有 item,所以挂起; 2、协程启动要时间,send(it) 后调用,发送 item,然后 输入 “send1”; 3、 协程循环再调用 send(it),此时队列已经满了,所以挂起,并唤起接收协程,然后 输入 “receive1”; 3、接收协程... ... ...

    作者回复: 是的。

    2022-04-10
    3
  • Geek_8a5ee1
    可以讲一下viewModelScope的区别吗

    作者回复: viewModelScope是Android当中的内容,三言两语讲不清,我会尝试在Android篇里加入一些内容,请留意之后的第34讲的内容。

    2022-03-07
    3
  • êwěn
    Recieve的cancel是清空channel中的消息,但不会close吧? 像go中如果在consumer 中关闭,会导致sender的panic。感觉kotlin也有这种陷阱。

    作者回复: 在Kotlin当中,我们调用cancel()以后,管道中的消息会被清空,同时,管道也会标记为cancel,这时候,我们已经可以认为它是关闭状态了。上游如果继续发送的话,会产生异常。具体可以看看下面这个代码: ``` fun main() = runBlocking { // 无限容量的管道 val channel = Channel<Int>(Channel.UNLIMITED) { println("onUndeliveredElement = $it") } // 等价这种写法 // val channel = Channel<Int>(Channel.UNLIMITED, onUndeliveredElement = { println("onUndeliveredElement = $it") }) // 放入三个数据 (1..3).forEach { channel.send(it) } // 取出一个,剩下两个 channel.receive() // 取消当前channel println(channel.isClosedForReceive) println(channel.isClosedForSend) channel.cancel() println(channel.isClosedForReceive) println(channel.isClosedForSend) channel.send(4) } ```

    2022-03-02
    2
    3
  • Paul Shan
    对于接收方而言,热的Channel状态是时刻改变的,数据之间是强依赖。简单的情况还好,如果Channel的数据级联了几次之后,调试就成了噩梦,这和滥用EventBus一样。

    作者回复: 没错,所以多个Channel组合的情况,我们需要三思而后行。

    2022-03-24
    2
  • 白乾涛
    以上代码看起来是可以正常工作了。但是,我仍然不建议你用这种方式。因为,当你为管道指定了 capacity 以后,以上的判断方式将会变得不可靠!原因是目前的 1.6.10 版本的协程库,运行这样的代码会崩溃,如下所示: ------------- 这是现象,而不是原因呀,具体原因是什么呢?

    作者回复: 原因就是:到目前为止isClosedForReceive是ExperimentalCoroutinesApi,它的功能仍然是有缺陷的。当Channel有capacity 以后,Kotlin官方支持的并不好,所以我们暂时不应该用它。

    2022-03-12
    2
  • 魏全运
    为什么例子中是ReceiveChannel 在send?

    作者回复: 你说的produce{}吗?在它的Lambda当中,我们是可以调用send()方法的,而它的返回值是ReceiveChannel。这不是一个概念哈。

    2022-03-08
    2
    1
  • Xs.Ten
    老师好,请问Channel 里面 Sender 和 Receiver 的身份可以发生互换么?

    作者回复: Channel的接口本身是通过组合得来的,理论上我们是可以让它们互换的,但不建议这么干。

    2022-03-26
  • 梁中华
    public fun <E> Channel( capacity: Int = RENDEZVOUS, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, onUndeliveredElement: ((E) -> Unit)? = null ): Channel<E> Channel居然也是个方法,为啥方法名是大写字母开头的? 难道是因为它是顶层方法?

    作者回复: 这是Kotlin 命名的一种特色。

    2022-03-21
    3
收起评论
显示
设置
留言
21
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部