Kotlin 协程 - 多路复用 select()

一、概念

又叫选择表达式,是一个挂起函数,可以同时等待多个挂起结果,只取用最快恢复的那个值(即多种方式获取数据,哪个更快返回结果就用哪个)。

同时到达 select() 会优先选择先写子表达式,想随机(公平)的话使用 selectUnbiased() 替换 。

能被选择的都是 SelectClauseN 函数类型。

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() -> Unit): R |
| public sealed interface SelectBuilder<in R> { public operator fun SelectClause0.invoke(block: suspend () -> R) public operator fun <Q> SelectClause1<Q>.invoke(block: suspend (Q) -> R) public operator fun <P, Q> SelectClause2<P, Q>.invoke(param: P, block: suspend (Q) -> R) public operator fun <P, Q> SelectClause2<P?, Q>.invoke(block: suspend (Q) -> R): Unit = invoke(null, block) } |

|---------------|---------------------------------------------------------------------------------------------|
| SelectClause0 | 对应事件没有返回值。例如 job.onJoin。 |
| SelectClause1 | 对应事件有返回值。例如 deffered.onAwait 和 channel.onReceive。 |
| SelectClause2 | 对应事件有返回值。此外还需要一个额外的参数,例如 Channel.onSend() 有两个参数,第一个是 Channel 数据类型的值表示即将发送的值,第二个是发送成功时的回调函数。 |

二、使用

在使用 async() 启动协程的返回类型 Deferred 中,定义了 SelectClause1 函数类型的变量 onAwait,其作用和 await() 一样,只是当其在 select() 中作为子语句时,具有"同时等待看谁最先返回"的效果。同理其它。

2.1 复用多个 job.onJoin

Kotlin 复制代码
fun main() = runBlocking<Unit> {
    val job1 = launch {
        delay(100)
        println("job 1")
    }
    val job2 = launch {
        delay(10)
        println("job 2")
    }
    select {
        job1.onJoin { println("job 1 更快") }
        job2.onJoin { println("job 2 更快") }
    }
    delay(1000)
}
//打印:
//job 2
//job 2 更快
//job 1

2.2 复用多个 deffered.onAwait

|-------------------------------------------------------------------------------------------------|
| public interface Deferred<out T> : Job { public val onAwait: SelectClause1<T> //等效await() } |

Kotlin 复制代码
fun main() = runBlocking {
    val defferedCache = async {
        delay(10)
        "Cache"
    }
    val defferedLocal = async {
        delay(100)
        "Local"
    }
    val defferedRemote = async {
        delay(1000)
        "Remote"
    }
    val result = select {
        defferedCache.onAwait { println("最快的是$it") }
        defferedLocal.onAwait { println("最快的是$it") }
        defferedRemote.onAwait { println("最快的是$it") }
    }
    delay(2000)
    println(result) //打印:最快的是Cache
}

2.3 复用多个 channel.onReceive

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public interface SendChannel<in E> { public val onSend: SelectClause2<E, SendChannel<E>> //等效send() } |
| public interface ReceiveChannel<out E> { public val onReceive: SelectClause1<E> //等效receive() public suspend fun receiveCatching(): ChannelResult<E> //等效receiveCatching() } //select() 中的 onReceive() 在已经关闭的通道执行会发生失败,并导致相应的 select() 抛出异常,使用 onReceiveCatching() 在关闭通道时执行特定操作。 |

Kotlin 复制代码
suspend fun getDataFromLocal() = withContext(Dispatchers.IO) { "Local" }
suspend fun getDataFromRemote() = withContext(Dispatchers.IO) { "Remote" }

@OptIn(ExperimentalCoroutinesApi::class)
fun main() = runBlocking {
    val produceLocal = produce { send(getDataFromLocal()) }
    val produceRemote = produce { send(getDataFromRemote()) }
    val result = select {
        produceLocal.onReceive { it }
        produceRemote.onReceive { it }
    }
//    val result = select {
//        produceLocal.onReceiveCatching { it.getOrNull() ?: "Channel已关闭:produceLocal" }
//        produceRemote.onReceiveCatching { it.getOrNull() ?: "Channel已关闭:produceRemote " }
//    }
    println("结果更快的是:$result")
}
相关推荐
技术liul1 小时前
使用安卓平板,通过USB数据线(而不是Wi-Fi)来控制电脑(版本1)
android·stm32·电脑
_祝你今天愉快3 小时前
Android FrameWork - 开机启动 & Init 进程 初探
android
2501_916007473 小时前
iOS App 上架实战 从内测到应用商店发布的全周期流程解析
android·ios·小程序·https·uni-app·iphone·webview
TimeFine4 小时前
Android 邮件发送日志
android
杨过过儿4 小时前
【Task02】:四步构建简单rag(第一章3节)
android·java·数据库
Wgllss4 小时前
Kotlin 享元设计模式详解 和对象池及在内存优化中的几种案例和应用场景
android·架构·android jetpack
zzywxc7876 小时前
AI 行业应用:金融、医疗、教育、制造业领域的落地案例与技术实现
android·前端·人工智能·chrome·金融·rxjava
sTone873756 小时前
android studio之外使用NDK编译生成android指定架构的动态库
android·c++
胖虎17 小时前
Android 入门到实战(三):ViewPager及ViewPager2多页面布局
android·viewpager·viewpager2
风往哪边走9 小时前
Media3在线本地视频播放器
android