[Kotlin] 스터디 Kotlin Coroutine: Deep Dive 12장
12장(2부) 에 해당하는 내용입니다.
12장. 디스패처
코루틴이 실행할 스레드(스레드풀)를 결정합니다.
Dispatchers의 코드를 따라가보면, 디스패처 또한 CoroutineContext 임을 확인할 수 있습니다.
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
디스패처 설정은 withContext를 이용합니다.
withContext를 호출하면 코루틴이 중단되고 큐에서 대기하다가 재개되기 때문에 비용이 듭니다.
viewModelScope.launch {
withContext(Dispatchers.IO) { }
}
디스패처의 종류
Main
- UI 와 상호작용하는 스레드를 사용하는 디스패처
Main.immediate
- 코드가 이미 Main 디스패처에서 호출된 경우 스레드 배정 없이 코드 즉시 실행
Unconfined
- 스레드 스위칭이 일어나지 않는 디스패처
- 동작할 스레드를 별도로 지정해두지 않은 디스패처이기 때문에, 코루틴이 실행, 재개된 스레드에서 계속 작업을 이어감
- 모든 작업을 Unconfined에서 실행하는 경우, 연산이 모두 같은 스레드에서 실행됨
- 블로킹이 일어나는 코드에서 사용 주의!
실제로 Unconfined가 스레드를 변경하지 않는지 확인해봅시다. 디스패처는 다르지만 같은 main 스레드에서 동작하는 것을 확인할 수 있습니다.
viewModelScope.launch(Dispatchers.Main) {
val a = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher
Log.d("coroutine", a.toString())
Log.d("coroutine", Thread.currentThread().name)
withContext(Dispatchers.Unconfined) {
val b = coroutineContext[ContinuationInterceptor] as CoroutineDispatcher
Log.d("coroutine", b.toString())
Log.d("coroutine", Thread.currentThread().name)
}
}
// 출력
Dispatchers.Main
main
Dispatchers.Unconfined
main
Default
- Kotlin 기본 디스패처
- CPU 집약적 연산 수행
- 스레드 풀 내 스레드 수는 일반적으로 CPU 수
IO
- 파일을 읽고 쓰거나 스레드를 블로킹할 때 사용하는 디스패처
- 스레드 풀 내 스레드 수는 일반적으로 코어 수
Default, IO는 (스레드가 무한한) 같은 스레드 풀을 공유합니다.
실제로 아래 코드를 실행해보면, 둘 다 같은 스레드에서 실행됨을 확인할 수 있습니다. (대부분의 경우)
fun main() = runBlocking(CoroutineName("parent")) {
withContext(Dispatchers.Default) {
println(Thread.currentThread().name)
withContext(Dispatchers.IO) {
println(Thread.currentThread().name)
}
}
}
// 출력
DefaultDispatcher-worker-1
DefaultDispatcher-worker-1
Default와 IO 디스패처는 같은 스레드 풀을 공유하지만, 스레드 수 제한은 각각 적용되므로 다른 디스패처의 스레드를 고갈시키지는 않습니다.
limitedParallelism
limitedParallelism
을 통해 같은 시간에 사용되는 스레드 수를 제한할 수 있습니다.
private val dispatcher = Dispatchers.Default.limitedParallelism(5)
Default와 IO에 사용할 때의 결과가 다릅니다.
- Dispatchers.Default 에 사용
- 디스패처에 스레드 수 제한 추가
- Dispatchers.IO 에 사용
- 새로운 스레드 풀을 가진 디스패처 생성
- 스레드를 자주 블로킹하는 클래스에서 활용
limitedParallelism(1)을 이용하여 스레드 수를 1로 제한하여, 공유 상태 문제를 해결할 수도 있습니다.
성능 정리
- 블로킹 작업의 경우, 스레드 수가 많을수록 모든 코루틴이 종료되는 시간이 빨라집니다.
- 블로킹 작업이 100개라고 가정하면,
- 스레드가 60일 때, 60개 먼저 작업 후 나머지 40개가 동작
- 스레드가 100개일 때, 100개 모두 같이 동작
- CPU 집약적 연산의 경우 Dispatchers.Default가 가장 좋습니다.
- 스레드가 많을수록 스레드 스위칭 시간이 늘어나기 때문에 더 많은 시간이 필요함
- Dispatchers.IO는 사용하지 않는 것이 좋음
- 블로킹 용도이기 때문에, 전체 스레드 블록 가능성이 있음
- 메모리 집약적 연산(대부분 시간이 메모리 접근, 할당 등에 사용되는 작업)은 더 많은 스레드를 사용하는 것이 좋습니다.
Comments