728x90
코틀린 코루틴을 요약한 내용입니다.
- 코루틴은 가장 먼저 완료되는 코루틴의 결과를 기다리는 select 함수를 제공
💡 select 함수는 코틀린 코루틴이 정식으로 출시된 이후부터 사용이 가능했지만, 여전히 실험용
지연되는 값 선택하기
- 여러 개의 소스에 데이터를 요청한 뒤, 가장 빠른 응답만 얻는 경우
- 요청을 여러 개의 비동기 프로세스로 시작
- select 함수를 표현식으로 사용하고 값을 기다린다
- 비동기 결과값 하나만 반환하는 예제
- 하나의 비동기 작업이 완료됨과 동시에 끝나게 되어 결과값 반환
suspend fun requestData1(): String { delay(100_000) return "Data1" } suspend fun requestData2(): String { delay(1000) return "Data2" } val scope = CoroutinScope(SupervisioJob()) suspend fun askMultipleForData(): String { val defData1 = scope.async { requestData1() } val defData2 = scope.async { requestData2() } return select { defData1.onAwait { it } defData2.onAwait { it } } } suspend fun main(): Unit = coroutineScope { println(askMultipleForData()) }
- coroutinScope를 사용하면 자식 코루틴도 기다리게 됨
- 1초가 아닌 100초 후에 Data2를 결과로 받을 수 있다
suspend fun askMultipleForData(): String = coroutineScope { select<String> { async { requestData1() }.onAwait { it } async { requestData2() }.onAwait { it } } } suspend fun main(): Unit = coroutineScope { println(askMultipleForData()) }
- async와 select를 사용하면 코루틴끼리 경합하는 상황을 쉽게 구현 가능
- 스코프를 명시적으로 취소 해야함
- select가 값을 생성하고 나서 also를 호출한 뒤 다른 코루틴을 취소
suspend fun askMultipleForData(): String = coroutineScope { select<String> { async { requestData1() }.onAwait { it } async { requestData2() }.onAwait { it } }.also { coroutineContext.cancelChildren() } } suspend fun main(): Unit = coroutineScope { println(askMultipleForData()) }
- raceOf 함수를 지원하는 외부 라이브러리를 사용하여 스코프를 명시적으로 취소해야하는 문제 해결
suspend fun askMultipleForData(): String = raceOf({
requestData1()
}, {
requestData2()
})
suspend fun main(): Unit = coroutineScope {
println(askMultipleForData())
}
채널에서 값 선택하기
- select 함수는 채널에서도 사용 가능
- onReceive
- 채널이 값을 가지고 있을 때 선택
- 값을 받은 뒤 람다식의 인자로 사용
- onReceive가 선택되었을 때, select는 람다식의 결과값을 반환
- onReceiveCatching
- 채널이 값을 가지고 있거나 닫혔을 때 선택
- 값을 나타내거나 채널이 닫혔다는 걸 알려주는 ChannelResult를 받으며, 이 값을 람다식의 인자로 사용
- onReceiveCatching이 선택되었을 때, select는 람다식의 결과를 반환
- onSend
- 채널의 버퍼에 공간이 있을 때 선택
- 채널에 값을 보낸 뒤, 채널의 참조값으로 람다식을 수행
- onSend가 선택되었을 때, select는 Unit을 반환
- 셀렉트 표현식은 여러 개의 채널로부터 결과값을 얻기 위해 onReceive나 onReceiveCatching과 함께 사용
suspend fun CoroutineScope.produceString(
s: String,
time: Long
) = produce {
while (true) {
delay(time)
send(s)
}
}
fun main() = runBlocking {
val fooChannel = produceString("foo", 210L)
val barChannel = produceString("Bar", 500L)
repeat(7) {
select {
fooChannel.onReceive {
println("From fooChannel: $it")
}
barChannel.onReceive {
println("From barChannel: $it")
}
}
}
coroutineContext.cancelChildren()
}
- 셀렉트 함수에서 onSend를 호출하면 버퍼에 공간이 있는 채널을 선택해 데이터를 전송하는 용도로 사용 할 수 있음
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")
}
}
}
요약
- select는 가장 먼저 완료되는 코루틴의 결과값을 기다릴 때나 여러 개의 채널 중 전송 또는 수신 가능한 채널을 선택할 때 유용
- 채널에서 작동하는 다양한 패턴을 구현할 때 사용 가능
- async 코루틴의 경합을 구현할 때 사용 가능
728x90
'코틀린 스터디' 카테고리의 다른 글
플로우란 무엇인가? (1) | 2024.01.04 |
---|---|
핫 데이터 소스와 콜드 데이터 소스 (0) | 2024.01.04 |
채널 (0) | 2023.12.04 |
코루틴 스코프 만들기 (1) | 2023.11.24 |
코루틴 기반 동시성 프로그래밍 (0) | 2023.11.24 |