Kotlin Coroutine - 셀렉트

Enso·2025년 6월 1일

Android-Kotlin-Coroutine

목록 보기
13/13

셀렉트 (Select)

코루틴은 가장 먼저 완료되는 코루틴의 결과를 기다리는 select 함수를 제공합니다.
또한 여러 개의 채널 중 버퍼에 남은 공간이 있는 채널을 먼저 확인하여 데이터를 보내거나, 이용 가능한 원소가 있는 채널로부터 데이터를 받을 수 있는지 여부도 확인할 수 있습니다.

지연되는 값 선택하기

여러 개의 소스에 데이터를 요청한 뒤 가장 빠른 응답만 얻는 경우를 코루틴만을 이용해서 생각해보면 async 코루틴 빌더로 여러개의 데이터를 병렬적으로 호출한 뒤 각 코루틴에서 await()로 기다리고 응답이 오면 존재하는 모든 코루틴을 취소 하는 방향으로 설계를 해야합니다. 하지만 select에서는 함수를 표현식으로 사용하고 표현식 내부에서 값을 기다리는 것만으로 처리할 수 있습니다.

select 내부에서는 셀렉트 표현식에서 나올 수 있는 결괏값을 명시하는 Deffered 값의 OnAwait 함수를 호출합니다.

fun main() = runBlocking {
    println(askMultipleForData())
}

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

suspend fun requestData2(): String {
    delay(1000)
    return "Data2"
}

val scope = CoroutineScope(SupervisorJob())

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

채널에서 값 선택하기

select 함수는 채널에서도 사용할 수 있습니다.

  • onReceive: 채널이 값을 가지고 있을 때 선택 됩니다. 값을 받은 뒤 람다식의 인자로 사용합니다. onReceive가 선택되었을 때, selec는 람다식의 결괏값을 반환합니다.
  • onReceiveCatcing: 채널이 값을 가지고 있거나 닫혔을 때 선택됩니다. 값을 나타내거나 채널이 닫혔다는 걸 알려주는 ChannelResult를 받으며, 이 값을 람다식의 인자로 사용합니다. onReceiveCatcing이 선택되었을 때, select는 람다식의 결괏값을 반환합니다.
  • onSend: 채널의 버퍼에 공간이 있을 때 선택됩니다. 채널에 값을 보낸 뒤, 채널의 참조값으로 람다식을 수행합니다. onSend가 선택 되었을 때, select는 Unit을 반환합니다.
fun main() = runBlocking {
    val fooChannel = produceSting("foo", 210L)
    val barChannel = produceSting("bar", 500L)

    repeat(7) {
        select {
            fooChannel.onReceive {
                println("From fooChannel: $it")
            }

            barChannel.onReceive {
                println("From barChannel: $it")
            }
        }
    }

    fooChannel.cancel()
    barChannel.cancel()
}

suspend fun CoroutineScope.produceSting(
    s: String,
    time: Long
) = produce {
    while (true) {
        delay(time)
        send(s)
    }
}

onReceive 이용해서 값을 가지고 있는 채널에서 값을 가져와 출력하는 코드 입니다.


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

    launch {
        for (c in 'A'..'H') {
            delay(400)
            select<Unit> {
                c1.onSend(c) { println("Sent $c to 1") }
                c2.onSend(c) { println("Sent $c to 2") }
            }
        }
    }

    launch {
        while (true) {
            delay(1000)
            val c = select<String> {
                c1.onReceive { "$it from 1" }
                c2.onReceive { "$it from 2" }
            }

            println("Received $c")
        }
    }
}

onSen를 통해 버퍼에 공간이 남아있는 채널을 선택해서 원소를 보내는 예시 코드 입니다.

profile
문제를 먼저 파악하고 대처할 수 있는 개발을 지향합니다.

0개의 댓글