Select 표현식은 아직 실험적인 기능으로 동작이 변경되거나 하위 호환 제공이 안될 수도 있다.

개요

select 표현식은 여러 중단 함수를 호출 후 그 결과를 동시에 대기할 수 있도록 해주고, 그 중 가장 먼저 완료된 결과를 이용할 수 있도록 해준다.

fun CoroutineScope.fizz() = produce<String> {
    while (true) { // sends "Fizz" every 300 ms
        delay(300)
        send("Fizz")
    }
}

fun CoroutineScope.buzz() = produce<String> {
    while (true) { // sends "Buzz!" every 500 ms
        delay(500)
        send("Buzz!")
    }
}

fun main(args: Array<String>) = runBlocking<Unit> {
    val fizz = fizz()
    val buzz = buzz()
    repeat(7) {
        selectFizzBuzz(fizz, buzz)
    }
    coroutineContext.cancelChildren() // cancel fizz & buzz coroutines
}

suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
    select<Unit> {
        // <Unit> means that this select expression does not produce any result
        fizz.onReceive { value ->
            // this is the first select clause
            println("fizz -> '$value'")
        }
        buzz.onReceive { value ->
            // this is the second select clause
            println("buzz -> '$value'")
        }
    }
}
fizz -> ‘Fizz’
buzz -> ‘Buzz!’
fizz -> ‘Fizz’
fizz -> ‘Fizz’
buzz -> ‘Buzz!’
fizz -> ‘Fizz’
buzz -> ‘Buzz!’

select 표현식 내부에 onReceive 함수를 통해 여러 채널에서 동시에 수신 받도록 구현할 수 있다.

닫힌 채널에서 select

select 블록 안에 onReceive 함수는 채널이 닫히면 예외를 전달하고 실패함

→ 이 경우 onReceiveCatching 함수 사용가능

Untitled

Untitled

위의 코드를 보면 a 채널이 우선 순위를 가짐에도 b 채널에 send가 실행됨

→ 채널은 기본적으로 버퍼가 0이기 때문에 send 사이에 중단이 생김

Untitled

Untitled

채널에 버퍼를 줄 경우 a 채널에만 send 실행됨을 볼 수 있음

편향 특성을 이용한 onSend