Kotlin协程通信机制: Channel
Coroutines Channels
Java中的多线程通信, 总会涉及到共享状态(shared mutable state)的读写, 有同步, 死锁等问题要处理.
协程中的Channel用于协程间的通信, 它的宗旨是:
Do not communicate by sharing memory; instead, share memory by communicating.
本文被收录在: https://github.com/mengdd/KotlinTutorials
Channel basics
channels用于协程间的通信, 允许我们在不同的协程间传递数据(a stream of values).
生产者-消费者模式
发送数据到channel的协程被称为producer, 从channel接受数据的协程被称为consumer.
生产: send, produce.
消费: receive, consume.
当需要的时候, 多个协程可以向同一个channel发送数据, 一个channel的数据也可以被多个协程接收.
当多个协程从同一个channel接收数据的时候, 每个元素仅被其中一个consumer消费一次. 处理元素会自动将其从channel里删除.
Channel的特点
Channel在概念上有点类似于BlockingQueue, 元素从一端被加入, 从另一端被消费. 关键的区别在于, 读写的方法不是blocking的, 而是suspending的.
在为空或为满时. channel可以suspend它的send和receive操作.
Channel的关闭和迭代
Channel可以被关闭, 说明没有更多的元素了.
取消producer协程也会关闭channel.
在receiver端有一种方便的方式来接收: 用for迭代.
看这个例子:
fun main() = runBlocking<Unit> {
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x)
channel.close() // we're done sending
}
// here we print received values using `for` loop (until the channel is closed)
for (y in channel) println(y)
println("Done!")
}
运行后会输出:
1
2
3
4
5
Done!
Process finished with exit code 0
如果注释掉channel.close()就会变成:
1
2
3
4
5
Done没有被输出, 程序也没有退出, 这是因为接受者协程还在一直等待.
不同的Channel类型
库中定义了多个channel类型, 它们的主要区别在于:
- 内部可以存储的元素数量;
send是否可以被挂起.
所有channel类型的receive方法都是同样的行为: 如果channel不为空, 接收一个元素, 否则挂起.
Channel的不同类型:
- Rendezvous channel: 0尺寸buffer,
send和receive要meet on time, 否则挂起. (默认类型). - Unlimited channel: 无限元素,
send不被挂起. - Buffered channel: 指定大小, 满了之后
send挂起. - Conflated channel: 新元素会覆盖旧元素, receiver只会得到最新元素,
send永不挂起.
创建channel:
val rendezvousChannel = Channel<String>()
val bufferedChannel = Channel<String>(10)
val conflatedChannel = Channel<String>(CONFLATED)
val unlimitedChannel = Channel<String>(UNLIMITED)
默认是Rendezvous channel.
练习: 分析代码输出
看这段代码:
fun main() = runBlocking<Unit> {
val channel = Channel<String>()
launch {
channel.send("A1")
channel.send("A2")
log("A done")
}
launch {
channel.send("B1")
log("B done")
}
launch {
repeat(3) {
val x = channel.receive()
log(x)
}
}
}
fun log(message: Any?) {
println("[${Thread.currentThread().name}] $message")
}
这段代码创建了一个channel, 传递String类型的元素.
两个producder协程, 分别向channel发送不同的字符串, 发送完毕后打印各自的"done".
一个receiver协程, 接收channel中的3个元素并打印.
程序的运行输出结果会是怎样呢?
记得在Configurations中加上VM options: -Dkotlinx.coroutines.debug. 可以看到协程信息.
答案揭晓:
[main @coroutine#4] A1
[main @coroutine#4] B1
[main @coroutine#2] A done
[main @coroutine#3] B done
[main @coroutine#4] A2
答对了吗?
为什么会是这样呢? 原因主要有两点:
- 这里创建的channel是默认的Rendezvous类型, 没有buffer, send和receive必须要meet, 否则挂起.
- 两个producer和receiver协程都运行在同一个线程上, ready to be resumed也只是加入了一个等待队列, resume要按顺序来.
这个例子在Introduction to Coroutines and Channels中有一个视频解说.
另外, 官方文档中还有一个ping-pang的例子, 为了说明Channels are fair.
参考
- 官方文档: Channels
- Introduction to Coroutines and Channels
- Github: Coroutines Guide
- Kotlin: Diving in to Coroutines and Channels
欢迎关注微信公众号: 圣骑士Wind

Kotlin协程通信机制: Channel的更多相关文章
- Kotlin 协程一 —— 全面了解 Kotlin 协程
一.协程的一些前置知识 1.1 进程和线程 1.1.1基本定义 1.1.2为什么要有线程 1.1.3 进程与线程的区别 1.2 协作式与抢占式 1.2.1 协作式 1.2.2 抢占式 1.3 协程 二 ...
- rxjava回调地狱-kotlin协程来帮忙
本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- Android Kotlin协程入门
Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...
- Kotlin协程解析系列(上):协程调度与挂起
vivo 互联网客户端团队- Ruan Wen 本文是Kotlin协程解析系列文章的开篇,主要介绍Kotlin协程的创建.协程调度与协程挂起相关的内容 一.协程引入 Kotlin 中引入 Corout ...
- Kotlin协程第一个示例剖析及Kotlin线程使用技巧
Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...
- Retrofit使用Kotlin协程发送请求
Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用.本文简单介绍一下Retrofit中协程的使用 导入依赖 app的build文件中加入: impleme ...
- Openresty Lua协程调度机制
写在前面 OpenResty(后面简称:OR)是一个基于Nginx和Lua的高性能Web平台,它内部集成大量的Lua API以及第三方模块,可以利用它快速搭建支持高并发.极具动态性和扩展性的Web应用 ...
- Kotlin协程基础
开发环境 IntelliJ IDEA 2021.2.2 (Community Edition) Kotlin: 212-1.5.10-release-IJ5284.40 我们已经通过第一个例子学会了启 ...
随机推荐
- java中的时区转换
目录 java中的时区转换 一.时区的说明 二.时间的表示 三.时间戳 四.Date类和时间戳 五.java中的时区转换 java中的时区转换 一.时区的说明 地球表面按经线从东到西,被划成一个个区域 ...
- 9、pytest -- 集成文档测试
目录 1. 集成doctest模块 1.1. 通过指定文本文件的方式 1.2. 通过编写文档字符串的方式 1.3. 指定额外的选项 2. 失败时继续执行 3. 指定输出的格式 4. 文档测试中使用fi ...
- win10+MinGw+ffmpeg 编译
一.安装MinGw+msys 下载 mingw-get-setup.exe 并安装,安装完成会弹出以下界面. 选中红色框几个选项,点击Installation->Apply Changes 进行 ...
- 爬取bing背景图片
因为工作环境的原因,没办法用梯子,也不喜欢用某度,只能用bing,发现背景图片蛮好看的,刚好最近在学习摄影,需要提高审美,就想着把bing背景图片都爬去下来做桌面背景.写的代码比较入门,只是做个记录, ...
- DOS打印目录树到文件
tree /f >>tree.txt 卷 数据 的文件夹 PATH 列表 卷序列号为 -FBAE E:. └─mysite │ manage.py │ └─mysite settings. ...
- UWP 带左右滚动按钮的横向ListView———仿NetFlix首页河的设计
也是之前写的控件了,模仿NetFlix的河的设计. 大体要求如下: 1. 横向的ListView 2. 左右按钮,可以左右移动河卡片,左右的滚动条不可见 3. 左右按钮仅在鼠标Hover事件中可见 大 ...
- 主席树学习笔记(静态区间第k大)
题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出 ...
- introduce new products
Today's the day. I'm giving you the heads up. Our company is rolling up its new line of cell phones. ...
- python、C++经典算法题:打印100以内的素数
题目 打印100以内的素数 思路1 素数的特点: 素数一定是奇数 一个数如果是合数,那么它一定能够被2到这个合数的开平方内的某个素数整除(这个特点是提升效率的关键) 一个数如果不能被从2到它自身开平方 ...
- drf
跨域同源 django做跨域同源 需要把csrf去掉 跨站请求伪造 同源 同源机制:域名.协议.端口号相同的同源 简单请求 不写头部请求 跨域会拦截报错缺少请求信息 (1) 请求方法是以下三种方法之一 ...