Skip to the content.

코루틴 컨텍스트와 디스패처

예제 27: 코루틴 디스패처

코루틴의 여러 디스패처 Default, IO, Unconfined, newSingleThreadContext을 사용해봅시다.

import kotlinx.coroutines.* fun main() = runBlocking<Unit> { launch { println("부모의 콘텍스트 / ${Thread.currentThread().name}") } launch(Dispatchers.Default) { println("Default / ${Thread.currentThread().name}") } launch(Dispatchers.IO) { println("IO / ${Thread.currentThread().name}") } launch(Dispatchers.Unconfined) { println("Unconfined / ${Thread.currentThread().name}") } launch(newSingleThreadContext("Fast Campus")) { println("newSingleThreadContext / ${Thread.currentThread().name}") } }
  1. Default는 코어 수에 비례하는 스레드 풀에서 수행합니다.
  2. IO는 코어 수 보다 훨씬 많은 스레드를 가지는 스레드 풀입니다. IO 작업은 CPU를 덜 소모하기 때문입니다.
  3. Unconfined는 어디에도 속하지 않습니다. 지금 시점에는 부모의 스레드에서 수행될 것입니다.
  4. newSingleThreadContext는 항상 새로운 스레드를 만듭니다.

예제 28: async에서 코루틴 디스패처 사용

launch외에 async, withContext 등의 코루틴 빌더에도 디스패처를 사용할 수 있습니다.

import kotlinx.coroutines.* fun main() = runBlocking<Unit> { async { println("부모의 콘텍스트 / ${Thread.currentThread().name}") } async(Dispatchers.Default) { println("Default / ${Thread.currentThread().name}") } async(Dispatchers.IO) { println("IO / ${Thread.currentThread().name}") } async(Dispatchers.Unconfined) { println("Unconfined / ${Thread.currentThread().name}") } async(newSingleThreadContext("Fast Campus")) { println("newSingleThreadContext / ${Thread.currentThread().name}") } }

예제 29: Confined 디스패처 테스트

Confined는 처음에는 부모의 스레드에서 수행됩니다. 하지만 한번 중단점(suspension point)에 오면 바뀌게 됩니다.

import kotlinx.coroutines.* fun main() = runBlocking<Unit> { async(Dispatchers.Unconfined) { println("Unconfined / ${Thread.currentThread().name}") delay(1000L) println("Unconfined / ${Thread.currentThread().name}") } }

Confined는 중단점 이후 어느 디스패처에서 수행될지 예측하기 어렵습니다. 가능하면 확실한 디스패처를 사용합시다.

예제 30: 부모가 있는 Job과 없는 Job

코루틴 스코프, 코루틴 컨텍스트는 구조화되어 있고 부모에게 계층적으로 되어 있습니다. 코루틴 컨텍스트의 Job 역시 부모에게 의존적입니다. 부모를 캔슬했을 때의 영향을 확인해보세요.

import kotlinx.coroutines.* fun main() = runBlocking<Unit> { val job = launch { launch(Job()) { println(coroutineContext[Job]) println("launch1: ${Thread.currentThread().name}") delay(1000L) println("3!") } launch { println(coroutineContext[Job]) println("launch2: ${Thread.currentThread().name}") delay(1000L) println("1!") } } delay(500L) job.cancelAndJoin() delay(1000L) }

job.cancelAndJoin() 실행 후의 delay가 없다면 어떻게 될까요?

예제 31: 부모의 마음

구조화되어 계층화된 코루틴은 자식들의 실행을 지켜볼까요?

import kotlin.system.* import kotlinx.coroutines.* fun main() = runBlocking<Unit> { val elapsed = measureTimeMillis { val job = launch { // 부모 launch { // 자식 1 println("launch1: ${Thread.currentThread().name}") delay(5000L) } launch { // 자식 2 println("launch2: ${Thread.currentThread().name}") delay(10L) } } job.join() } println(elapsed) }

부모를 join해서 기다려 보면 부모는 두 자식이 모두 끝날 때까지 기다린다는 것을 알 수 있습니다.

예제 32: 코루틴 엘리먼트 결합

여러 코루틴 엘리먼트를 한번에 사용할 수 있다. + 연산으로 엘리먼트를 합치면 된다. 합쳐진 엘리먼트들은 coroutineContext[XXX]로 조회할 수 있다.

import kotlin.system.* import kotlinx.coroutines.* fun main() = runBlocking<Unit> { launch { launch(Dispatchers.IO + CoroutineName("launch1")) { println("launch1: ${Thread.currentThread().name}") println(coroutineContext[CoroutineDispatcher]) println(coroutineContext[CoroutineName]) delay(5000L) } launch(Dispatchers.Default + CoroutineName("launch1")) { println("launch2: ${Thread.currentThread().name}") println(coroutineContext[CoroutineDispatcher]) println(coroutineContext[CoroutineName]) delay(10L) } } }