๐ก ์ฝ๋ฃจํด ๋น๋์์ ํ์ฉ๋๋ ์ฝ๋ฃจํด ์ปจํ ์คํธ์ ๋ํ์ฌ ํ์ตํ์์ต๋๋ค.
์ฝ๋ฃจํด ์ปจํ ์คํธ
- ์ฝ๋ฃจํด ๋น๋์ ์ ์๋ฅผ ๋ณด๋ฉด ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๊ฐ CoroutineContext์์ ์ ์ ์์ต๋๋ค.
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
- ๋ฆฌ์๋ฒ๋ฟ๋ง ์๋๋ผ ๋ง์ง๋ง ์ธ์์ ๋ฆฌ์๋ฒ๋ CoroutineScope ํ์ ์ด๋ฉฐ, ์ค์ํ ๊ฐ๋ ์ผ๋ก ํ์ฉ๋๋ CoroutineContext์ ์ ์๋ฅผ ์์์ผ ํฉ๋๋ค !
CoroutineContext ์ธํฐํ์ด์ค
- CoroutineContext๋ ์์๋ ์์๋ค์ ์งํฉ์ ๋ํ๋ด๋ ์ธํฐํ์ด์ค์ ๋๋ค.
- ์ปจํ ์คํธ์ ์ง์ ๊ณผ ๋ณ๊ฒฝ์ ํธ๋ฆฌํ๊ฒ ํ๊ธฐ ์ํด CoroutineContext์ ๋ชจ๋ ์์๋ CourtineContext๋ก ๋์ด ์์ต๋๋ค.
- ๋ํ ์ปจํ ์คํธ์์ ๋ชจ๋ ์์๋ ์๋ณํ ์ ์๋ ์ ์ผํ Key๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
CoroutineContext์์ ์์ ์ฐพ๊ธฐ
- CoroutineContext๋ ์ปฌ๋ ์ ๊ณผ ๊ฐ๋ ์ด ๋น์ทํ๊ธฐ ๋๋ฌธ์ get์ ์ฌ์ฉํ ์ ์ผํ ํค๋ฅผ ๊ฐ์ง ์์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
- ์์๋ฅผ ์ฐพ์ง ๋ชปํ๋ค๋ฉด Null์ด ๋ฐํ๋ฉ๋๋ค.
fun main() {
val ctx: CoroutineContext = CoroutineName("A name")
val coroutineName: CoroutineName? = ctx[CoroutineName] //ctx.get(Job)
val job: Job? = ctx[job]
println(job) // null
}
- CoroutineContext๋ ์ฝํ๋ฆฐ ์ฝํฌํด์ด ์ธ์ด์ ์ผ๋ก ์ง์ํ๊ธฐ ์๊ธฐ ๋๋ฌธ์ kotlin.coroutines์์ import๋ฉ๋๋ค.
- ํ์ง๋ง Job์ด๋ CoroutineName๊ณผ ๊ฐ์ ์ปจํ ์คํธ๋ kotlinx.coroutines ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ importํฉ๋๋ค.
์ปจํ ์คํธ ๋ํ๊ธฐ
- CoroutineContext๋ ๋ ๊ฐ์ CoroutineContext๋ฅผ ํฉ์ณ ํ๋์ CoroutineContext๋ก ๋ง๋ค ์ ์๋ค๋ ํน์ง์ด ์์ต๋๋ค.
- ๋ค๋ฅธ ํค๋ฅผ ๊ฐ์ง ๋ ์์๋ฅผ ๋ํ๋ฉด ๋ง๋ค์ด์ง ์ปจํ ์คํธ๋ ๋ ๊ฐ์ง ํค๋ฅผ ๋ชจ๋ ๊ฐ์ง๋๋ค.
fun main() {
val ctx1: CoroutineContext = CoroutineName("name1")
println(ctx1[CoroutineName]?.name) //name1
println(ctx1[Job]?.isActive) // null
val ctx2: CoroutineContext = Job()
println(ctx2[CoroutineName]?.name) // null
println(ctx2[Job]?.isActive) // true
val ctx3 = ctx1 + ctx2
println(ctx3[CoroutineName]?.name) // name1
println(ctx2[Job]?.isActive) // true
}
์ปจํ ์คํธ ์์ ์ ๊ฑฐ
- minusKey ํจ์์ ํค๋ฅผ ๋ฃ๋ ๋ฐฉ์์ผ๋ก ์์๋ฅผ ์ปจํ ์คํธ์์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
fun main() {
val ctx1: CoroutineContext = CoroutineName("name1") + Job()
println(ctx1[CoroutineName]?.name) //name1
println(ctx1[Job]?.isActive) // true
val ctx2: ctx1.minusKey(CoroutineName)
println(ctx2[CoroutineName]?.name) // null
println(ctx2[Job]?.isActive) // true
val ctx3 = (ctx1 + CoroutineName("name2")).minusKey(CoroutineName)
println(ctx3[CoroutineName]?.name) // null
println(ctx2[Job]?.isActive) // true
}
์ปจํ ์คํธ ํด๋ฉ
- ์ปจํ ์คํธ์ ๊ฐ ์์๋ฅผ ์กฐ์ํด์ผ ํ๋ ๊ฒฝ์ฐ ๋ค๋ฅธ ์ปฌ๋ ์ ์ fold์ ์ ์ฌํ ๋ฉ์๋๋ฅผ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
[fold]
- ์ปฌ๋ ์ ์ ์ํํ๋ฉด์ ๊ฐ ์์๋ฅผ ๋์ ํ์ฌ ํ๋์ ๊ฐ์ ์์ฑํ๋ ๊ณ ๊ณ ํจ์์ ๋๋ค.
- ์ด๊ธฐ๊ฐ๊ณผ ๋์ ํจ์๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์, ์ปฌ๋ ์ ์ ๊ฐ ์์์ ๋ํด ๋์ ํจ์๋ฅผ ์ ์ฉํ์ฌ ์ต์ข ๊ฐ์ ๋ฐํํฉ๋๋ค.
fun main() {
val numbers = listOf(1,2,3,4,5)
val sum = numbers.fold(0) { acc, number ->
acc + number
}
println(sum) // 15
}
inline fun <T, R> Iterable<T>.fold(
initial: R,
operation: (acc: R, T) -> R
): R
- CoroutineContext์์ fold๋ฅผ ์๋ ๊ท์น์ ํตํด ์ ์ฉํ ์ ์์ต๋๋ค.
- ๋์ฐ๊ธฐ์ ์ฒซ ๋ฒ์งธ ๊ฐ์ ๋ฃ์ด์ค
- ๋์ฐ๊ธฐ์ ํ์ฌ ์ํ์ ํ์ฌ ์คํ๋๊ณ ์๋ ๋์ฐ๊ธฐ์ ๋ค์ ์ํ๋ฅผ ๊ณ์ฐํ ์ฐ์ฐ ์งํ
fun main() {
val ctx1: CoroutineContext = CoroutineName("name1")
ctx1.fold("") { acc, element -> "$acc$element" }
.also(::println)
}
์ฝํฌ๋ฆฐ ์ปจํ ์คํธ์ ๋น๋
- CoroutineContext๋ ์ฝ๋ฃจํด์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
- ๋ถ๋ชจ-์์ ๊ด๊ณ์ ์ํฅ ์ค ํ๋๋ก ๋ถ๋ชจ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ปจํ ์คํธ๋ฅผ ์์์๊ฒ ์ ๋ฌํฉ๋๋ค.
- ์์์ ๋ถ๋ชจ๋ก๋ถํฐ ์ปจํ ์คํธ๋ฅผ ์์๋ฐ๋๋ค๊ณ ํ ์ ์์ต๋๋ค.
fun CoroutineScope.log(msg: String) {
val name = coroutineContext[CoroutineName]?.name
println("[$name] $msg")
}
fun main() = runBlocking(CorutineName("main")) {
log("Started") //[main] Started
val v1 = async {
delay(500)
log("Running async") // [main] Running async
42
}
launch {
delay(1000)
log(Running launch") // [main] Running launch
}
log("the answer is ${v1.await()}")
// [main] the answer is 42
}
- ๋ฌผ๋ก ๋ชจ๋ ์์์ ๋น๋์ ์ธ์์์ ์ ์๋ ํน์ ์ปจํ ์คํธ๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ๋ถ๋ชจ๋ก๋ถํฐ ์์๋ฐ์ ์ปจํ ์คํธ๋ฅผ ๋์ฒดํ ์ ์์ต๋๋ค.
- ์๋์ ๊ฐ์ ๊ณต์์ด ์ ์ฉ๋ฉ๋๋ค.
newContext = defaultContext + parentContext + childContext
- defaultContext๋ ContinuationInterceptor๊ฐ ์ค์ ๋์ง ์์์ ๋ ์ฌ์ฉํ๋ Dispathcers.Default์ด๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋๋ฒ๊ทธ ๋ชจ๋์ผ ๋๋ CoroutineId๋ ๋ํดํธ๋ก ์ค์ ๋ฉ๋๋ค.
- Job์ ํญ์ ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ฉฐ, ์ฝ๋ฃจํด์ ์์๊ณผ ๋ถ๋ชจ๊ฐ ์ํตํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํน๋ณํ ์ปจํ ์คํธ์ ๋๋ค.
์ค๋จ ํจ์์์์ ์ปจํ ์คํธ
- CoroutineScope๋ ์ปจํ ์คํธ๋ฅผ ์ ๊ทผํ ๋ ์ฌ์ฉํ๋ coroutineContext ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- ๋ฐ๋ผ์ ์ผ๋ฐ์ ์ธ ์ค๋จ ํจ์์์๋ ์ปจํ ์คํธ์ ์ ๊ทผ ๊ฐ๋ฅํ๋ฉฐ, ๊ฐ๋ณ์ ์ผ๋ก ์์ฑ๋ ๊ฐ๋ฅํฉ๋๋ค.
// ์ค๋จ ํจ์์์ ์ปจํ
์คํธ ์ ๊ทผ
suspend fun printName() {
println(coroutineContext[CoroutineName]?.name)
}
suspend fun main() = withContext(CoroutineName("Outer")){
printName() // Outer
launch(CoroutineName("Inner"){
printName() // Inner
}
delay(10)
printName() // Outer
}
// ์ปจํ
์คํธ ์์ฑ
class MyCustomContext: CoroutineContext.Element {
override val key : CoroutineContext.Key<*> = Key
companion object Key :
CoroutineCotext.Key<MyCustomContext>
}
- ์ปจํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ๋ ๋งค์ฐ ์ ์ง๋ง, ํ ์คํธ ํ๊ฒฝ๊ณผ ํ๋ก๋์ ํ๊ฒฝ์์ ์๋ก ๋ค๋ฅธ ๊ฐ์ ์ฝ๊ฒ ์ฃผ์ ํ๊ธฐ ์ํด ๊ฐ๋ ์ฌ์ฉํฉ๋๋ค.
์์ฝ
- coroutineContext๋ ๋งต์ด๋ ์งํฉ๊ณผ ๊ฐ์ ์ปฌ๋ ์ ์ด๋ ๊ฐ๋ ์ ์ผ๋ก ๋น์ทํฉ๋๋ค.
- coroutineContext๋ Element ์ธํฐํ์ด์ค์ ์ธ๋ฑ์ฑ๋ ์งํฉ์ด๋ฉฐ, Element ๋ํ coroutineContext์ ๋๋ค.
- coroutineContext ์์ ๋ชจ๋ ์์๋ ์๋ณํ ๋ ์ฌ์ฉ๋๋ ์ ์ผํ Key๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- ์ด๋ ์ฝ๋ฃจํด์ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ ๊ฐ์ฒด๋ก ๊ทธ๋ฃนํํ๊ณ ์ ๋ฌํ๋ ๋ณดํธ์ ์ด ๋ฐฉ๋ฒ์ด๋ฉฐ, ์ฝ๋ฃจํด์ ์ ์ฅ๋ฉ๋๋ค.
- coroutineContext๋ฅผ ์ฌ์ฉํด์ ์ฝ๋ฃจํด์ ์ํ๋ฅผ ํ์ธํ๊ณ , ์ด๋ค ์ค๋ ๋๋ฅผ ์ ํํ ์ง ๋ฑ ์ฝ๋ฃจํด์ ์๋ ๋ฐฉ์์ ์ ํ ์ ์์ต๋๋ค.