본문 바로가기

코루틴

[코루틴] select

 

select 함수

가장 먼저 완료되는 코루틴의 결과를 기다리는 함수

 

 

지연되는 값 선택

suspend fun main(): Unit = coroutineScope {
    println(askMultipleForData())
}

val scope = CoroutineScope(SupervisorJob())

suspend fun askMultipleForData(): String {
    val data1 = scope.async { requestData1() }
    val data2 = scope.async { requestData2() }
    return select {
        data1.onAwait { it }
        data2.onAwait { it }
    }
}

suspend fun requestData1(): String {
    delay(10_000)
    return "Data1"
}

suspend fun requestData2(): String {
    delay(1000)
    return "Data2"
}
// 결과 (1초후에)
Data2

 

 

코루틴끼리 경합

suspend fun main(): Unit = coroutineScope {
    println(askMultipleForData())
}

suspend fun askMultipleForData(): String = coroutineScope {
    select<String> {
        async { requestData1() }.onAwait { it }
        async { requestData2() }.onAwait { it }
    }.also { coroutineContext.cancelChildren() }
}
// 결과 (1초 후)
Data2

 

 

채널에서 값 선택하기

select는 여러 개의 채널 중 버퍼에 남은 공간이 있는 채널을 확인해 데이터를 보내거나

이용 가능한 원소가 있는 채널로부터 데이터를 받을 수 있는지 확인 가능

 

select 에서 사용하는 함수

 

onReceive

채널이 값을 가지고 있을 때 선택된다. select는 람다식의 결과값 반환

 

onReceiveCatching

채널이 값을 가지고 있거나 닫혔을 때 선택된다. ChannelResult 받아 사용. select는 람다식의 결과값 반환

 

onSend

채널의 버퍼에 공간이 있을 때 선택된다. 채널에 값을 보낸 뒤 채널의 참조값으로 람다식 수행. select는 Unit 반환

 

 

onReceive 사용

fun main() = runBlocking {
    val channel1 = produceString("channel1", 210L)
    val channel2 = produceString("channel2", 500L)

    repeat(7) {
        select {
            channel1.onReceive {
                println("From channel1: $it")
            }
            channel2.onReceive {
                println("From channel2: $it")
            }
        }
    }
    
    coroutineContext.cancelChildren()
}

suspend fun CoroutineScope.produceString(
    s: String,
    time: Long
) = produce {
    while (true) {
        delay(time)
        send(s)
    }
}
// 결과
From channel1: channel1
From channel1: channel1
From channel2: channel2
From channel1: channel1
From channel1: channel1
From channel2: channel2
From channel1: channel1

 

 

onSend 호출

(버퍼에 공간이 있는 채널을 선택해 데이터 전송)

fun main(): Unit = runBlocking {
    val channel1 = Channel<Char>(capacity = 2)
    val channel2 = Channel<Char>(capacity = 2)

    // 값 send
    launch {
        for (c in 'A'..'H') {
            delay(400)
            select<Unit> {
                channel1.onSend(c) { println("Sent $c to 1") }
                channel2.onSend(c) { println("Sent $c to 2") }
            }
        }
    }

    // 값 receive
    launch {
        while (true) {
            delay(1000)
            val c = select<String> {
                channel1.onReceive { "$it from 1"}
                channel2.onReceive { "$it from 2"}
            }
            println("Received $c")
        }
    }
}
// 결과
Sent A to 1
Sent B to 1
Received A from 1
Sent C to 1
Sent D to 2
Received B from 1
Sent E to 1
Sent F to 2
Received C from 1
Sent G to 1
Received E from 1
Sent H to 1
Received G from 1
Received H from 1
Received D from 2
Received F from 2