디스페처 - 어떤 쓰레드에서 수행될지 결정
코루틴 디스패처
코루틴의 여러 디스패처 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}")
}
}
수행결과
Default / DefaultDispatcher-worker-1 @coroutine#3
Unconfined / main @coroutine#5
IO / DefaultDispatcher-worker-2 @coroutine#4
부모의 콘텍스트 / main @coroutine#2
newSingleThreadContext / Fast Campus @coroutine#6
디스페처의 종류
- Default는 코어 수에 비례하는 스레드 풀에서 수행합니다. ( 복잡한 연산 )
- IO는 코어 수 보다 훨씬 많은 스레드를 가지는 스레드 풀입니다. IO 작업은 CPU를 덜 소모하기 때문입니다. ( 파일읽어오기 또는 네트워크 통신 )
- Unconfined는 어디에도 속하지 않습니다. 지금 시점에는 부모의 스레드에서 수행될 것입니다. (앞으로 어디에서 수행될지 모르는 특징을 가지고 있다. )
- newSingleThreadContext는 항상 새로운 스레드를 만듭니다. (이름을 지정해줄수 있다. )
* Default, IO 는 스레드 풀에 있는 스레드를 사용하기 때문에
다른 스레드가 이미 많이 쓰이고 있는경우 할당받지 못할 수 도 있다.
( 항상 할당받아야 하는경우에는 newSingleThreadContext 를 쓸 수 있다. )
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}")
}
}
사용예시
Unconfined / main @coroutine#5
Default / DefaultDispatcher-worker-1 @coroutine#3
IO / DefaultDispatcher-worker-3 @coroutine#4
부모의 콘텍스트 / main @coroutine#2
newSingleThreadContext / Fast Campus @coroutine#6
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는 중단점 이후 어느 디스패처에서 수행될지 예측하기 어렵습니다.
가능하면 확실한 디스패처를 사용합시다.
( 사용하지 않는 것을 추천 한다. )
부모가 있는 Job과 없는 Job
코루틴 스코프, 코루틴 컨텍스트는 구조화되어 있고 부모에게 계층적으로 되어 있습니다.
코루틴 컨텍스트의 Job 역시 부모에게 의존적입니다. 부모를 캔슬했을 때의 영향을 확인해보세요.
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> { // 증부모
val job = launch { // 부모
launch(Job()) { // 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)
}
실행결과
"coroutine#3":StandaloneCoroutine{Active}@b684286
launch1: main @coroutine#3
"coroutine#4":StandaloneCoroutine{Active}@36d64342
launch2: main @coroutine#4
3! // 위에서 Job을 만들어 버려서 부모가 누군지 모른다.
job.cancelAndJoin() 실행 후의 delay가 없다면 어떻게 될까요?
=> delay(1000L) 을 없애면, 종료된다.
val job을 기다리고 있었는데 Job()이 자식이 아니기때문에 시스템은 기다려주지 않고 종료 .
부모의 마음
구조화되어 계층화된 코루틴은 자식들의 실행을 지켜볼까요?
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)
}
실행결과
launch1: main @coroutine#3
launch2: main @coroutine#4
5024
부모를 join해서 기다려 보면 부모는 두 자식이 모두 끝날 때까지 기다린다는 것을 알 수 있습니다.
코루틴 엘리먼트 결합
여러 코루틴 엘리먼트를 한번에 사용할 수 있다. + 연산으로 엘리먼트를 합치면 된다.
합쳐진 엘리먼트들은 coroutineContext[XXX]로 조회할 수 있다.
디스페처, 네임 등을 붙일 수 있다.
import kotlin.system.*
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
launch {
// 엘리먼트1 Dispatchers.IO 엘리먼트 2 CoroutineName("launch1")
launch(Dispatchers.IO + CoroutineName("launch1")) { // 얘네를 Coroutine Context 라고 불른다.
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)
}
}
}
* 부모의 컨텍스트를 연결해서 만들어진다.
* 앨리먼트끼리 + 로 연결해준 것을 컨텍스트라고 하고, 넣고 빼고를 할 수 있다.
코루틴 내에서 Context를 접근 할수 있다
coroutineContext[CoroutineDispatcher] // 디스페처명 가져오기
coroutineContext[CoroutineName] // 코루틴 이름 가지고 오기
'Android 공부 > Coroutine' 카테고리의 다른 글
[XX캠퍼스] 07.Kotlin Coroutines & Flow (공유객체, Mutex, Actor ) (0) | 2022.07.25 |
---|---|
[XX캠퍼스] 06. Kotlin Coroutines & Flow ( CEH와 슈퍼바이저 잡 ) (0) | 2022.07.25 |
[XX캠퍼스] 04. Kotlin Coroutines & Flow ( 서스팬딩 함수 ) (0) | 2022.07.22 |
[XX캠퍼스] 03. Kotlin Coroutines & Flow ( 취소와 타임아웃 ) (0) | 2022.07.21 |
[XX캠퍼스] 02. Kotlin Coroutines & Flow ( 잡,구조화된동시성 ) (0) | 2022.07.20 |