Table of contents
Open Table of contents
CoroutineContext
인터페이스
CoroutineContext
는 원소나 원소들의 집합을 나타내는 인터페이스입니다.- 컨텍스트의 지정과 변경을 편리하게 하기 위해
CoroutineContext
의 모든 원소가CoroutineContext
로 되어 있습니다. - 컨텍스트에서 모든 원소는 식별할 수 있는 유일한 키를 가지고 있습니다. 각 키는 주소로 비교가 됩니다.
CoroutineContext
에서 원소 찾기
CoroutineContext
는 컬렉션과 비슷하기 때문에 get
함수를 사용하여 원소를 찾을 수 있습니다.
fun main() {
val ctx: CoroutineContext = CoroutineName("A name")
val coroutineName: CoroutineName? = ctx[CoroutineName]
println(coroutineName?.name) // A name
val job: Job? = ctx[Job]
println(job) // null
}
컨텍스트 더하기
CoroutineContext
의 정말 유용한 기능은 두 개의CoroutineContext
를 합쳐 하나의CoroutineContext
로 만들 수 있는 것입니다.- 다른 키를 가진 두 원소를 더하면 만들어진 컨텍스트는 두 가지 키를 모두 가집니다.
fun main() {
val ctx1: CoroutineContext = CoroutineName("A name")
val ctx2: CoroutineContext = Job()
val ctx3 = ctx1 + ctx2
val coroutineName: CoroutineName? = ctx3[CoroutineName]
println(coroutineName?.name) // A name
val job: Job? = ctx3[Job]
println(job) // JobImpl{Active}@7b23ec81
}
CoroutineContext
에 같은 키를 가진 또 다른 원소가 더해지면 맵처럼 새로운 원소가 기존 원소를 대체합니다.
fun main() {
val ctx1: CoroutineContext = CoroutineName("A name")
val ctx2: CoroutineContext = CoroutineName("Another name")
val ctx3 = ctx1 + ctx2
val coroutineName: CoroutineName? = ctx3[CoroutineName]
println(coroutineName?.name) // Another name
}
비어 있는 코루틴 컨텍스트
CoroutineContext
는 컬렉션이므로 빈 컨텍스트 또한 만들 수 있습니다. 빈 컨텍스트는 원소가 없으므로, 다른 컨텍스트에 더해도 아무런 변화가 없습니다.
fun main() {
val ctx1: CoroutineContext = CoroutineName("A name")
val ctx2: CoroutineContext = EmptyCoroutineContext
val ctx3 = ctx1 + ctx2
val coroutineName: CoroutineName? = ctx3[CoroutineName]
println(coroutineName?.name) // A name
}
원소 제거
minusKey
함수에 키를 넣는 방식으로 원소를 컨텍스트에서 제거할 수 있습니다.
fun main() {
val ctx1: CoroutineContext = CoroutineName("A name")
val ctx2: CoroutineContext = Job()
val ctx3 = ctx1 + ctx2
val ctx4 = ctx3.minusKey(CoroutineName)
val coroutineName: CoroutineName? = ctx4[CoroutineName]
println(coroutineName) // null
}
컨텍스트 폴딩
컨텍스트의 각 원소를 조작해야 하는 경우 다른 컬렉션의 fold
와 유사한 fold
메서드를 사용할 수 있습니다.
fun main() {
val ctx: CoroutineContext = CoroutineName("A name") + Job()
val result = ctx.fold("Coroutine context elements: ") { acc, element ->
acc + "\n${element.key}: ${element.value}"
}
println(result)
}
코투린 컨텍스트와 빌더
CoroutineContext
는 코루틴의 데이터를 저장하고 전달하는 방법입니다.- 부모는 기본적으로 컨텍스트를 자식에게 전달합니다.
fun CoroutineScope.log(msg: String) {
val name = coroutineContext[CoroutineName]?.name
println("$name: $msg")
}
fun main() = runBlocking(CoroutineName("main")) {
log("Started") // main: Started
val v1 = async {
delay(500)
log("Running async") // main: Running async
42
}
lanuch {
delay(1000)
log("Running launch") // main: Running launch
}
log("The answer is ${v1.await()}") // main: The answer is 42
}
- 모든 자식은 빌더의 인자에서 정의된 특정 컨텍스트를 가질 수 있습니다.
- 코루틴 컨텍스트를 계산하는 간단한 공식은 다음과 같습니다.
defaulContext = parentContext + childElement
- 새로원 원소가 같은 키를 가진 이전 원소를 대체하므로, 자식의 컨텍스트는 부모로부터 상속ㅂ다은 컨텍스트 중 같은 키를 가진 원소를 대체합니다.
중단 함수에서 컨텍스트에 접근하기
CoroutineScope
는 컨텍스트를 접근할 때 사용하는coroutineContext
프로퍼티를 가지고 있습니다.coroutineContext
프로퍼티는 모든 중단 스코프에서 사용이 가능하며, 이를 통해 컨텍스트에 접근할 수 있습니다.
suspend fun printName() {
println(coroutineContext[CoroutineName]?.name)
}
suspend fun main() = withContext(CoroutineName("Outer")) {
printName() // Outer
launch(CoroutineName("Inner")) {
printName() // Inner
}
delay(100)
printName() // Outer
}
요약
CoroutineContext
는 맵이나 셋같은 컬렉션이랑 개념적으로 비슷합니다.CoroutineContext
는Element
인터페이스의 인덱싱된 집합이며,Element
또한CoroutineContext
입니다.CoroutineContext
안에 모든 원소는 식별할 때 사용되는 유일한Key
를 가지고 있습니다.