协程:相互协作的程序
⼀些 API 启动⻓时间运⾏的操作(例如⽹络 IO、⽂件 IO、CPU 或 GPU 密集型任务等),并要求调⽤者阻塞直到它们完成。协程提供了⼀种避免阻塞线程并用更廉价、更可控的操作替代线程阻塞的⽅法:协程挂起
协程通过将复杂性放⼊库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器!)上调度执行,而代码则保持如同顺序执行⼀样简单
一、准备工作
由于协程目前还没有纳入kotlin标准库,所以要使用协程,需先进行以下配置
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'
}
二、协程的优点
- 将异步,回调,订阅等代码,简化成顺序执行代码一样
普通回调方式
var a返回值 = A任务()
B任务(a返回值){ b返回值 ->
//回调方法
C任务(b返回值){ c返回值 ->
//回调方法
D任务(c返回值)
}
}
loadDataFromNet {
println(it)
}
private fun loadDataFromNet(callback: (String) -> Unit){
Thread{
Thread.sleep(3000)
callback("这是网络请求后的结果")
}
}
使用协程
协程{
D任务( C任务( B任务( A任务() ) ) )
}
async {
val message = loadDataFromNet()
println(message)
}
private suspend fun loadDataFromNet() : String{
delay(3000)
return "这是网络请求后的结果"
}
- 轻量级线程
val jobs = List(100_000) {
launch {
delay(1000)
print("*")
}
}
launch {
jobs.forEach {
it.join()
}
}
开启了10w个协程,会正常打印出来。如果换成Thread会抛出 out of memory
三、创建一个协程
- launch 与 async
launch方法返回一个Job类型,并不会返回值;sync有返回值
//launch 创建的 是Job
val job = launch {
delay(1000)
println("launch")
}
//async 创建的 是Deferred, Deferred是Job的一个子类
val deferred = async {
delay(1000)
println("async")
return 1 //async 才能有return
}
runBlocking { }也是一个协程,不过一般不这样使用
- 指定线程
launch (UI){ } //UI 线程
launch (CommonPool){ } //普通线程
async(UI) { }
async(CommonPool){ }
- 协程的行为控制
delay(millis:Long):协程挂起
Deffered.join():类似Thread.join
Deffered.cancel():协程取消,可携带一个 Exception 实例作为参数,以在协程取消时抛出该异常,可以通过返回值判断协程是否取消成功
Deffered.await():等待直接获取协程返回值
四、挂起函数 suspend
被suspend 修饰的函数为挂起函数,挂起函数 只能在协程中调用
private suspend fun loadDataFromNet() : String{
delay(3000)
return "这是网络请求后的结果"
}
五、其他
yield
val fibonacci = buildSequence {
var cur = 1
var next = 1
while (true) {
yield(next)
val tmp = cur + next
cur = next
next = tmp
}
}
println(fibonacci.take(20).toList())
打印结果
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
Channels(通道)
之前说的都是一个一个协程独立工作,但是实际使用中协程经常会相互协作,比如生产者消费者。这其中很重要的一个东西就是协程之间的数据通信。Channel就是协程数据通信的一个重要手段。
fun channelTest() = runBlocking<Unit> {
val channel = Channel<Int>()
launch {
// this might be heavy CPU-consuming computation or async logic, we'll just send five squares
Log.d("sss","start send value")
for (x in 1..5) {
Log.d("sss","send value ${x*x}")
channel.send(x * x)
}
Log.d("sss","end send value")
}
// here we print five received integers:
repeat(5) { Log.d("sss",channel.receive().toString()) }
Log.d("sss","Done!")
}
// D/sss: start send value
// D/sss: send value 1
// D/sss: send value 4
// D/sss: 1
// D/sss: send value 9
// D/sss: 4
// D/sss: 9
// D/sss: send value 16
// D/sss: send value 25
// D/sss: 16
// D/sss: 25
// D/sss: Done!
// D/sss: end send value