首页 > 其他分享 >【Kotlin 协程】协程中的多路复用技术 ② ( select 函数原型 | SelectClauseN 事件 | 查看挂起函数是否支持 select )

【Kotlin 协程】协程中的多路复用技术 ② ( select 函数原型 | SelectClauseN 事件 | 查看挂起函数是否支持 select )

时间:2023-01-07 17:06:37浏览次数:41  
标签:协程 函数 多路复用 ReceiveChannel 子句 select


文章目录

  • ​​一、select 函数原型​​
  • ​​二、Select clause 事件​​
  • ​​1、SelectClause0 事件代码示例​​
  • ​​2、SelectClause2 事件代码示例​​
  • ​​三、查看挂起函数是否支持 select​​






一、select 函数原型



在上一篇博客 ​​【Kotlin 协程】协程中的多路复用技术 ① ( 多路复用技术 | await 协程多路复用 | Channel 通道多路复用 )​​ 中 , 介绍了 协程多路复用技术 , 多路复用 主要使用 select 代码块 实现 ,

  • 在 select 代码块中 调用多个协程的 onAwait 函数
// 同时执行两个协程, 哪个先执行完毕, 就取哪个协程的执行结果
val data = select<String> {
localJob.onAwait{it}
remoteJob.onAwait{it}
}
  • 在 select 代码块中 调用多个 Channel 通道的 onReceive 函数
val num = select<Int> {
channel0.onReceive {it}
channel1.onReceive {it}
}



上述多路复用都用到了 select 函数 , 其函数原型如下 :

/**
* 同时等待使用_clauses_指定的多个挂起函数的结果
* 在此选择调用的[builder]范围内。调用者被挂起,直到其中一个子句
* 是_selected_或_fails_。
*
* 最多一个子句被*原子地*选中,并且它的块被执行。所选子句的结果
* 成为选择的结果。如果有任何子句_fails_,则选择调用将生成
* 相应的异常。在本例中没有选择子句。
*
* 这个选择函数是_biased_到第一个子句。当可以同时选择多个子句时,
* 第一个有优先权。使用[selectUnbiased]表示无偏(随机)选择
* 条款。

* 选择表达式没有' default '子句。相反,每个可选择的挂起函数都具有
* 对应的非挂起版本,可以与常规的“when”表达式一起使用来选择一个
* 的选项,如果没有选项可以立即选择,则执行默认(' else ')操作。
*
* ###支持的选择方法列表
*
* | **Receiver** | **Suspending function** | **Select clause**
* | ---------------- | --------------------------------------------- | -----------------------------------------------------
* | [Job] | [join][Job.join] | [onJoin][Job.onJoin]
* | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait]
* | [SendChannel] | [send][SendChannel.send] | [onSend][SendChannel.onSend]
* | [ReceiveChannel] | [receive][ReceiveChannel.receive] | [onReceive][ReceiveChannel.onReceive]
* | [ReceiveChannel] | [receiveCatching][ReceiveChannel.receiveCatching] | [onReceiveCatching][ReceiveChannel.onReceiveCatching]
* | [Mutex] | [lock][Mutex.lock] | [onLock][Mutex.onLock]
* | none | [delay] | [onTimeout][SelectBuilder.onTimeout]
*
* 这个暂停函数是可以取消的。如果当前协程的[Job]被取消或完成
* 函数挂起后,该函数立即恢复[CancellationException]。
* 有**立即取消保证**。如果作业被取消,而此函数被取消
* 暂停,将无法成功恢复。参见[suspendCancellableCoroutine]文档了解底层细节。
*
* 注意,该函数在未挂起时不会检查是否取消。
* 使用[yield]或[CoroutineScope。isActive]如果需要,在紧循环中定期检查取消。
*/
public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() -> Unit): R {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val scope = SelectBuilderImpl(uCont)
try {
builder(scope)
} catch (e: Throwable) {
scope.handleBuilderException(e)
}
scope.getResult()
}
}






二、Select clause 事件



协程中的多路复用 主要是在 select 代码块中实现 ,

能够在 select 中执行的多路复用事件 , 称为 SelectClauseN 事件 :

  • SelectClause0 事件 : 没有返回值 , 没有参数 ; 如 : onJoin 事件 ;
  • SelectClause1 事件 : 有返回值 , 没有参数 ; 如 : onAwait 事件 和 onReceive 事件 ;
  • SelectClause2 事件 : 有返回值 , 有参数 ; 如 : Channel 通道的 onSend 事件 ;


挂起函数 如果存在对应的 SelectClauseN 事件 , 那么就可以使用 select 实现多路复用 ;



1、SelectClause0 事件代码示例



代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.selects.select

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

runBlocking {
val job0 = GlobalScope.launch {
delay(500)
println("job0")
}

val job1 = GlobalScope.launch {
delay(1000)
println("job1")
}

// 由于没有返回值 , select 后泛型为 Unit
select<Unit> {
job0.onJoin { println("job0.onJoin") }
job1.onJoin { println("job1.onJoin") }
}

// 等待所有协程执行完毕
delay(1500)
}
}
}

执行结果 :

23:17:27.355 System.out   kim.hsl.coroutine     I  job0
23:17:27.357 System.out kim.hsl.coroutine I job0.onJoin
23:17:27.861 System.out kim.hsl.coroutine I job1

【Kotlin 协程】协程中的多路复用技术 ② ( select 函数原型 | SelectClauseN 事件 | 查看挂起函数是否支持 select )_select



2、SelectClause2 事件代码示例



代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.selects.select

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

runBlocking {
val channel0 = Channel<Int>()
val channel1 = Channel<Int>()

launch (Dispatchers.IO) {
select<Unit> {
launch {
delay(500)
channel0.onSend(500) {
println("channel0 通道发送 500")
}
}

launch {
delay(1000)
channel1.onSend(1000) {
println("channel1 通道发送 1000")
}
}
}
}

GlobalScope.launch {
println("channel0 通道接收数据 : ${channel0.receive()}")
}

GlobalScope.launch {
println("channel1 通道接收数据 : ${channel1.receive()}")
}

// 确保上述协程执行完毕
delay(1500)
}
}
}

执行结果 :

23:26:10.202 System.out   kim.hsl.coroutine     I  channel0 通道接收数据 : 500
23:26:10.207 System.out kim.hsl.coroutine I channel0 通道发送 500

【Kotlin 协程】协程中的多路复用技术 ② ( select 函数原型 | SelectClauseN 事件 | 查看挂起函数是否支持 select )_协程_02






三、查看挂起函数是否支持 select



如果查看某个挂起函数是否支持 select , 直接进入该函数源码 , 查看其是否定义了对应的 SelectClauseN 类型 , 如查看 Channel#onSend 函数原型时 , 其声明了 onSend 类型为 SelectClause2<E, SendChannel<E>> , 说明这是一个 SelectClause2 事件 , 可用于 select 多路复用 ;

/**
* 子句用于[send]暂停函数的[select]表达式,该表达式在指定元素时进行选择
* 当参数被发送到通道时。子句被选中时,对该通道的引用
* 传递到相应的块中。
*
* 如果通道[为' send '关闭][isClosedForSend](参见[close]了解详细信息),则[select]调用失败并出现异常。
*/
public val onSend: SelectClause2<E, SendChannel<E>>



另外也可以参考下面的表格中的 Select clause 定义 , 这是 select 函数文档内容 :

Receiver

Suspending function

Select clause

[Job]

[join][Job.join]

[onJoin][Job.onJoin]

[Deferred]

[await][Deferred.await]

[onAwait][Deferred.onAwait]

[SendChannel]

[send][SendChannel.send]

[onSend][SendChannel.onSend]

[ReceiveChannel]

[receive][ReceiveChannel.receive]

[onReceive][ReceiveChannel.onReceive]

[ReceiveChannel]

[receiveCatching][ReceiveChannel.receiveCatching]

[onReceiveCatching][ReceiveChannel.onReceiveCatching]

[Mutex]

[lock][Mutex.lock]

[onLock][Mutex.onLock]

none

[delay]

[onTimeout]


标签:协程,函数,多路复用,ReceiveChannel,子句,select
From: https://blog.51cto.com/u_14202100/5995615

相关文章

  • 【Android OpenCV】Visual Studio 创建支持 OpenCV 库的 CMake 工程 ③ ( CMake 工程
    文章目录​​一、CMake工程中配置OpenCV库文件​​​​二、拷贝OpenCV库文件​​​​三、测试OpenCV​​一、CMake工程中配置OpenCV库文件在上一篇博客​​【An......
  • Oracle常⽤函数
    Oracle常⽤函数数值函数:selectabs(-5)fromdual;--的绝对值selectmod(521,10)fromdual;--521被10除后的余数selectpower(10,2)fromdual;--10的2次⽅select......
  • cmake常用函数
    cmake的常用函数cmake_minimum_required(VERSION3.0)#指定cmake的最小版本project(demo)#设置项目名称add_executable(demodemo.cpp)#生成可执行文件demo.cpp......
  • 一些文本处理的函数
    头文件一般文本_UNICODE未定义_UNICODE已定义函数功能推荐函数WinUser.hwvsprintfwvsprintfAwvsprintfW使用指向参数列表的指针将数据写入指定缓冲区。StringCb......
  • 生成函数
    生成函数1.OGF1.1.递归式和OGFExample1.考虑一个问题,一开始池塘里有\(50\)只青蛙,视作第\(0\)年。每年青蛙会翻四倍,然后跳出池塘\(100\)只,问第\(n\)年池塘有......
  • Python----函数进阶
    函数的返回值作为参数传递给其他函数deffunc():return50deffunc1(num):print(num+100)func1(func())函数返回多个值deffunc():#返回值可以是......
  • [ensp自学]5.dhcp select relay中继
    AR1:创建两个ippoolvlan10和20,g0口ipadd10.0.0.124,静态路由两条:iproute-static192.168.10.02410.0.0.2iproute-static192.168.20.02410.0.0.2s1:创建vlan1020,......
  • Vue项目中怎样把参数(对象)转成formdata传给后端? 封装函数 亲测有效
    普通传参格式如下:  想要的formData参数格式如下:  首先封装参数(对象)转换为formData格式getFormData(object){constformData=newFormData()......
  • __builtin_函数的使用
    typedefunsignedintui1.intffs(uix){//该函数判断n的二进制末尾最后一个1的位置,从一开始return__builtin_ffs(x);}2.intpopcount(uix){//该函数时判断n......
  • 积性函数
    唯一分解定理\(n=\prod\limits_{i=1}^mp_i^{k_i}\),此质因数分解式唯一。通常我们令\(p\)单调递增,称\(p\)的次数构成了的向量为质数-指数向量,即数字的另一种表示形......