1 前言
協程的 select 是一種用於非同步操作的選擇器,它允許同時等待多個掛起函式的結果,並在其中一個完成時執行相應的操作。
能夠被 select 的事件都是 SelectClause,在 select.kt 中有定義,如下。
public 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)
...
}
public interface SelectClause0 {
public fun <R> registerSelectClause0(select: SelectInstance<R>, block: suspend () -> R)
}
public interface SelectClause1<out Q> {
public fun <R> registerSelectClause1(select: SelectInstance<R>, block: suspend (Q) -> R)
}
public interface SelectClause2<in P, out Q> {
public fun <R> registerSelectClause2(select: SelectInstance<R>, param: P, block: suspend (Q) -> R)
}
internal class SelectBuilderImpl<in R>(
private val uCont: Continuation<R>
) : LockFreeLinkedListHead(), SelectBuilder<R>,
SelectInstance<R>, Continuation<R>, CoroutineStackFrame
{
override fun SelectClause0.invoke(block: suspend () -> R) {
registerSelectClause0(this@SelectBuilderImpl, block)
}
override fun <Q> SelectClause1<Q>.invoke(block: suspend (Q) -> R) {
registerSelectClause1(this@SelectBuilderImpl, block)
}
override fun <P, Q> SelectClause2<P, Q>.invoke(param: P, block: suspend (Q) -> R) {
registerSelectClause2(this@SelectBuilderImpl, param, block)
}
}
2 select 在 Job 中的應用
1)應用
fun main() {
CoroutineScope(Dispatchers.Default).launch {
var job1 = launchJob("job-1", 100)
var job2 = launchJob("job-2", 200)
var res = select {
job1.onJoin { "select: 1" }
job2.onJoin { "select: 2" }
}
println(res) // 列印: select: 1
}
Thread.sleep(1000) // 阻塞當前執行緒, 避免程式過早結束, 協程提前取消
}
suspend fun launchJob(tag: String, delayTime: Long): Job =
CoroutineScope(Dispatchers.Default).launch {
println("tag")
delay(delayTime)
}
列印如下。
tag
tag
select: 1
2)onJoin 原始碼
onJoin 是 Job 中定義的屬性。
public val onJoin: SelectClause0
說明:在呼叫 job1.onJoin { xxx } 時,等價於呼叫了 SelectClause0.invoke 函式,也等價於呼叫了 SelectClause0.registerSelectClause0 函式。
3 select 在 Deferred 中的應用
1)應用
fun main() {
CoroutineScope(Dispatchers.Default).launch {
var task1 = asyncTask("task-1", 100)
var task2 = asyncTask("task-2", 200)
var res = select {
task1.onAwait { "select: $it" }
task2.onAwait { "select: $it" }
}
println(res) // 列印: select: task-1
}
Thread.sleep(1000) // 阻塞當前執行緒, 避免程式過早結束, 協程提前取消
}
suspend fun asyncTask(tag: String, delayTime: Long): Deferred<String> =
CoroutineScope(Dispatchers.Default).async {
delay(delayTime)
tag
}
列印如下。
select: task-1
2)onAwait 原始碼
onAwait 是 Deferred 中定義的屬性。
public val onAwait: SelectClause1<T>
說明:在呼叫 task1.onAwait { xxx } 時,等價於呼叫了 SelectClause1.invoke 函式,也等價於呼叫了 SelectClause1.registerSelectClause1 函式。
4 select 在 Channel 中的應用
4.1 onSend
1)應用
fun main() {
var channels = List(2) { Channel<String>() }
CoroutineScope(Dispatchers.Default).launch {
var res = select {
channels[0].onSend("select-1") { "task-1" }
channels[1].onSend("select-2") { "task-2" }
}
println("res=$res") // 列印: res=task-2
}
receiveTask(200, channels[0])
receiveTask(100, channels[1])
Thread.sleep(1000) // 阻塞當前執行緒, 避免程式過早結束, 協程提前取消
}
fun receiveTask(delayTime: Long, channel: Channel<String>) {
CoroutineScope(Dispatchers.Default).launch {
delay(delayTime)
var element = channel.receive()
println("receive: $element")
}
}
列印如下。
receive: select-2
res=task-2
2)onSend 原始碼
onSend 是 Channel 中定義的屬性。
public val onSend: SelectClause1<E>
說明:在呼叫 channels[0].onSend(xxx) { yyy } 時,等價於呼叫了 SelectClause2.invoke 函式,也等價於呼叫了 SelectClause2.registerSelectClause2 函式。
4.2 onReceive
1)應用
fun main() {
var channels = List(2) { Channel<String>() }
sendTask("task-1", 200, channels[0])
sendTask("task-2", 100, channels[1])
CoroutineScope(Dispatchers.Default).launch {
var res = select {
channels[0].onReceive { "select: $it" }
channels[1].onReceive { "select: $it" }
}
println(res) // 列印: select: task-2
}
Thread.sleep(1000) // 阻塞當前執行緒, 避免程式過早結束, 協程提前取消
}
fun sendTask(tag: String, delayTime: Long, channel: Channel<String>) {
CoroutineScope(Dispatchers.Default).launch {
delay(delayTime)
channel.send(tag)
}
}
列印如下。
select: task-2
2)onReceive 原始碼
onReceive 是 Channel 中定義的屬性。
public val onReceive: SelectClause1<E>
說明:在呼叫 channels[0].onReceive { xxx } 時,等價於呼叫了 SelectClause1.invoke 函式,也等價於呼叫了 SelectClause1.registerSelectClause1 函式。
宣告:本文轉自【Kotlin】select簡介。