协程04 - 挂起函数的配合

本文介绍了如何在Kotlin中并行执行协程,包括使用`async`和`launch`的区别,以及协程的延迟启动模式。同时,文章还讨论了如何通过Channel解决协程间通信问题,如生产者消费者模型和不同类型的Channel(如Unlimited、Buffered、Rendezvous和Conflated)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

并行执行协程

假如我们有两个挂起函数:

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}

当我们想串行的执行它们时,直接按顺序调用即可:

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = doSomethingUsefulOne()
        val two = doSomethingUsefulTwo()
        println("The answer is ${one + two}")
    }
    println("Completed in $time ms")
}

当我们想并行执行时,应该如何做呢?使用 async 方法即可:

val time = measureTimeMillis {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")

async 与 launch 差不多,不过,launch 返回的是一个 Job,并不携带任何结果值,而 async 返回的是一个延迟(Deferred),一个轻量级的非阻塞Future,代表着稍后提供结果。

协程延迟启动

当我们使用 async 方式启动协程的时候,还可以给协程指定一下启动模式:

@JvmStatic
fun main(args: Array<String>) = runBlocking {
    val time = measureTimeMillis {
        val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
        val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
        // some computation
        delay(1000)
        one.start() // start the first one
        two.start() // start the second one
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

设置启动模式为 LAZY 后,只有当我们主动调用了 start 或者 await 方法时,协程才会开始执行。

输出结果:

The answer is 42
Completed in 2011 ms

可以看到,在 delay 1000 之前,协程并没有开始执行。

Channel

一般我们使用协程,都会让协程运行在子线程上,所以当协程多起来的时候,协程之间的合作与通信就是一个蛋疼的问题了。好在有了 Channel,它就类似于一个 BlockingQueue,用来处理生产者与消费者之间的同步问题。

 

当多个协程从同一channel接收信息时,每个元素只由其中一个消费者处理一次。一旦处理完一个元素,就会立即从channel中删除。

Channel 有 3 种:

interface SendChannel<in E> {
    suspend fun send(element: E)
    fun close(): Boolean
}

interface ReceiveChannel<out E> {
    suspend fun receive(): E
}

interface Channel<E> : SendChannel<E>, ReceiveChannel<E>

SendChannel 就是发送数据的。

ReceiveChannel 就是接收数据的。

Channel,这个玩意继承了上面两个,想来是一个组合体,一个对象可以实现通信。

Channel又分4种:

  • Unlimited channel,就是里面可以放无限多的数据

  • Buffered channel,有限的数据

 

  • Rendezvous channel,0个数据,发送会阻塞,除非有接收。接收会阻塞,除非有发送。

 

  • Conflated channel,新的会覆盖旧的,size = 1.

 

看一个通信的例子:

@JvmStatic
fun main(array: Array<String>) = runBlocking<Unit> {
    val channel = Channel<String>(Channel.CONFLATED)
    launch {
        channel.send("A1")
        channel.send("A2")
        log("A done")
    }
    launch {
        channel.send("B1")
        log("B done")
    }

    launch {
        channel.send("C1")
        log("C done")
    }
    launch {
        repeat(3) {
            val x = channel.receive()
            log(x)
        }
    }
}

最终输出结果:

[main] A done
[main] B done
[main] C done
[main] C1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二手的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值