使用场景
- 一个刷新操作需要2个网络请求才能完成刷新
- 需要查询两次数据库才能刷新界面
- 需要从两个硬件设备, 比如2个Camera, 读取两帧, 才能开始下一步的操作
非协程的方式, Java也具有这种方式
@Test
fun test_CompletableFuture() {
formatSop("test_CompletableFuture")
val future1 = CompletableFuture.supplyAsync {
formatSop("launch future1")
sleep(1000)
"future1 1000"
}
val future2 = CompletableFuture.supplyAsync {
formatSop("launch future2")
sleep(2000)
"future2 2000"
}
val future3 = CompletableFuture.supplyAsync {
formatSop("launch future3")
sleep(3000)
"future3 3000"
}
val result1 = future1.join()
formatSop("result1: $result1")
val result3 = future3.join()
formatSop("result3: $result3")
val result2 = future2.join()
formatSop("result2: $result2")
}
private fun sleep(timeMillis: Long) {
Thread.sleep(timeMillis)
}
private var startSeconds: Long = 0L
private fun formatSop(msg: String?) {
if (startSeconds == 0L) {
startSeconds = System.currentTimeMillis() / 1000
}
val date = System.currentTimeMillis() / 1000 - startSeconds
println("$date: $msg")
}
输出的日志如下
0: test_CompletableFuture
0: launch future1
0: launch future2
0: launch future3
1: result1: future1 1000
3: result3: future3 3000
3: result2: future2 2000
带result的都会在当前线程打印
带launch的分别对应不同的子线程
join方法会阻塞当前线程, 直到子线程执行完毕, 这样只有当所有子线程执行完毕, 最后一个当前线程的日志才会被打印
协程的方式
@Test
fun test_async() {
formatSop("test_async")
GlobalScope.launch {
val job1 = async {
formatSop("launch job1")
delay(1000)
"job1 1000"
}
val job2 = async {
delay(2000)
"job2 2000"
}
val job3 = async {
delay(3000)
"job3 3000"
}
val result1 = job1.await()
formatSop("result1: $result1")
val result3 = job3.await()
formatSop("result3: $result3")
val result2 = job2.await()
formatSop("result2: $result2")
}
Thread.sleep(10 * 1000)
}
private var startSeconds: Long = 0L
private fun formatSop(msg: String?) {
if (startSeconds == 0L) {
startSeconds = System.currentTimeMillis() / 1000
}
val date = System.currentTimeMillis() / 1000 - startSeconds
println("$date: $msg")
}
通过async执行异步任务, 然后通过wait获取异步任务的返回结果, 类似的原理和Java的join类似, 在调用wait之前就已经线程开始了, wait的作用只是为了获取协程的执行结果, 获取不到就wait.
上述代码的打印结果如下
0: test_async
0: launch job1
1: result1: job1 1000
3: result3: job3 3000
3: result2: job2 2000
协程Flow的方式
flow让我想起RxJava的一些操作符, 又麻烦又不实用, 平添记忆负担. 对flow的一种使用情况进行测试, 其他情况可能有能够异步执行的方式, 下面方式实际上没有做到多线程异步, 最后还是同步一个个的执行.
@Test
fun test_flow() {
formatSop("test_flow")
runBlocking<Unit> {
val flow1 = flow {
formatSop("launch flow1")
sleep(1000)
emit("flow1 1000")
}
val flow2 = flow {
formatSop("launch flow2")
sleep(2000)
emit("flow2 2000")
}
val flow3 = flow {
formatSop("launch flow3")
sleep(3000)
emit("flow3 3000")
}
// 同时启动并等待两个任务完成
launch {
flow1.collect { formatSop(it) }
flow3.collect { formatSop(it) }
flow2.collect { formatSop(it) }
}
launch {
}
launch {
}
}
}
private var startSeconds: Long = 0L
private fun formatSop(msg: String?) {
if (startSeconds == 0L) {
startSeconds = System.currentTimeMillis() / 1000
}
val date = System.currentTimeMillis() / 1000 - startSeconds
println("$date: $msg")
}
输出
0: test_flow
0: launch flow1
1: flow1 1000
1: launch flow3
4: flow3 3000
4: launch flow2
6: flow2 2000