Kotlin -协程学习的第八天

87 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

Channel的关闭

和消息队列不同,一个Channel可以通过被关闭来表明没有更多的元素将会进入通道。

然后接收者可以定期的使用for循环来从Channel中接收元素。

一个close()操作,就是向Channel发送了一个特殊的关闭指令。这个当这个关闭操作被

Channel收到的时候,通道就进入了迭代停止状态。也就是说之后通道将不会有数据更新了。它能够保证所有先前发送出去的元素都在Channel收到close消息前被接收到

fun main() = runBlocking {
    val channel = Channel<Int>()
    launch {
        for (x in 1..5) channel.send(x * x)
        channel.close() // 我们结束发送
    }
// 这里我们使用 `for` 循环来打印所有被接收到的元素(直到通道被关闭)
    for (y in channel) println(y)
    println("结束!")
}

  • produce和actor返回的Channel都会随着对应的协程执行完毕而关闭。正式因为这样Channel才被称为热数据流。
  • 对于一个Channel,如果我们调用流它的close方法。它会立即停止接收新元素,也就是说这时它的isClosedForSend立即返回true。而由于Channel缓冲区的存在,这个时候可能还有一些元素没有被处理完,因此要等所有元素都被读取之后isClosedForReceive才会返回true。
  • Channel的生命周期最好由主导方来维护,建议由主导的一方实现关闭。

BroadcastChannel

发送端和接收端在Channel中存在一对多的情形,从数据处理本身来讲,虽然有多个接收端,但是同一个元素只会一个接收端读到。广播则不然,多个接收端不存在互斥行为。

多路复用

数据通信系统或计算机网络系统中,传输媒体的带宽或容量往往会大于传输单一信号的需求,为了有效的需求,为了有效地利用通信线路,希望一个信道同时传输多路信号,这就是所谓的多路复用技术。

image.png

复用多个await

两个api分别从网络和本地缓存获取数据,期望哪个先返回就用哪个做展示。

image.png

复用多个Channel

和await类似,会接收到最快的那个channel消息

fun `test channel select`() = runBlocking {
    val channels = listOf(Channel<Int>(), Channel<Int>())

    GlobalScope.launch {
        delay(50)
        channels[0].send(0)
    }

    GlobalScope.launch {
        delay(100)
        channels[1].send(1)
    }

    val select = select<Int> {
        channels.forEach {
            it.onReceive { value ->
                value
            }
        }
    }

    println(select)
    delay(1000)
}

SelectCause

并不是所有的事件可以使用select的,只有SelectCauseN类型的事件
1.SelectCause0:对应事件没有返回值,例如join,那么onJoin就是SelectCauseN,使用时,onJoin的参数是一个无参函数
2.SelectCause1:对应事件有返回值,例如onAwait,onReceive
3.SelectCause3:对应事件有返回值,此外还要一个额外参数,例如Channel.onSend,一个参数为Channel数据类型的值,一个为发送成功时的回调

fun `test channel send select`() = runBlocking {
    val channels = listOf(Channel<Int>(), Channel<Int>())
    launch {
        select<Unit?> {
            launch {
                delay(100)
                channels[0].onSend(0) {
                    println("onSend 0")
                }
            }

            launch {
                delay(50)
                channels[1].onSend(1) {
                    println("onSend 1")
                }
            }
        }
    }

    GlobalScope.launch {
        println(channels[0].receive())
    }

    GlobalScope.launch {
        println(channels[1].receive())
    }
    
}

使用Flow实现多路复用

fun `test flow merge`() = runBlocking {
    listOf(::getInfoForLocal1, ::getInfoForLocal2)
        .map { function ->
            function.call()
        }.map { deferred ->
            flow { emit(deferred.await()) }
        }.merge()
        .collect {
            println(it)
        }
}