Dispatcher란?
- dispatch와 -er의 합성어
- dispatch의 `보내다`라는 뜻에 -er이 붙어 `무언가를 보내는 주체`라는 뜻
CoroutineDispatcher
- `코루틴을 보내는 주체`
- 코루틴은 일시 중단이 가능한 `작업`이기 때문에 쓰레드가 있어야 실행될 수 있음
- CoroutineDispatcher는 코루틴을 쓰레드로 보내 실행시키는 역할
- 코루틴을 쓰레드로 보내는 데 사용할 수 있는 쓰레드나 쓰레드 풀 가짐
- 코루틴을 실행 요청한 쓰레드에서 코루틴이 실행되도록 만들 수 있음
CoroutineDispatcher 동작 과정
- CoroutineDispatcher 객체에 코루틴의 실행이 요청됨
- CoroutineDispatcher 객체는 실행 요청받은 코루틴을 작업 대기열에 적재시킴
- CoroutineDispatcher 객체는 자신이 사용 가능한 쓰레드가 있는지 확인
- 작업 대기열에 적재된 코루틴을 사용 가능한 쓰레드로 보내 실행
* 사용 가능한 쓰레드가 없을 경우 작업 대기열에서 대기
제한된 디스패처와 무제한 디스패처
- CoroutineDispatcher에는 두 가지 종류가 존재
- 제한된 디스패처 (Confined Dispatcher)
- 무제한 디스패처 (Unconfined Dispatcher)
1. 제한된 디스패처
- 사용할 수 있는 쓰레드나 쓰레드 풀이 제한된 디스패처
- 앞서 `CoroutineDispatcher 동작 과정`에서 설명한 대로 동작함
- 일반적으로 CoroutineDispatcher 객체별로 어떤 작업을 처리할지 미리 역할을 부여하고 역할에 맞춰 실행을 요청하는 것이 효율적이기 때문에 대부분의 CoroutineDispatcher 객체는 제한된 디스패처
2. 무제한 디스패처
- 사용할 수 있는 쓰레드나 쓰레드 풀이 제한되지 않은 디스패처
- 실행할 수 있는 쓰레드가 제한되지 않았다고 해서 실행 요청된 코루틴이 아무 쓰레드에서나 실행되는 것은 아님
- 무제한 디스패처는 실행 요청된 코루틴이 이전 코드가 실행되던 쓰레드에서 계속해서 실행되도록 처리
- 이 때문에 실행되는 쓰레드가 매번 달라질 수 있고 특정 쓰레드로 제한되어 있지 않음
제한된 디스패처 생성하는 방법
- 코루틴 라이브러리는 사용자가 직접 제한된 디스패처를 만들 수 있도록 몇 가지 함수를 제공
1. 단일 쓰레드 디스패처 생성
- 사용할 수 있는 쓰레드가 하나인 CoroutineDispatcher 객체
- 코루틴 라이브러리에서 제공하는 `newSingleThreadContext` 함수를 사용해 생성 가능
- `newFixedThreadPoolContext` 함수에 쓰레드 개수 인자로 1을 전달하면 동일하게 생성 가능
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlinx.coroutines.CoroutineDispatcher | |
import kotlinx.coroutines.newSingleThreadContext | |
val dispatcher: CoroutineDispatcher = newSingleThreadContext(name = "SingleThread") |
2. 멀티 쓰레드 디스패처 생성
- 두 개 이상의 쓰레드를 사용할 수 있는 CoroutineDispatcher 객체
- 코루틴 라이브러리의 newFixedThreadPoolContext 함수를 사용해 생성 가능
- 만들어지는 쓰레드들은 인자로 받은 name 값 뒤에 `-1`부터 시작해 숫자가 하나씩 증가하는 형식으로 명명
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlinx.coroutines.CoroutineDispatcher | |
import kotlinx.coroutines.newFixedThreadPoolContext | |
val multiThreadDispatcher: CoroutineDispatcher = newFixedThreadPoolContext(2, "MultiThread") |

CoroutineDispatcher 사용해 코루틴 실행하는 방법
1. launch의 파라미터로 CoroutineDispatcher 사용
- launch 함수를 호출해 만든 코루틴을 특정 CoroutineDispatcher 객체에 실행 요청하기 위해서는 launch 함수의 context 인자로 CoroutineDispatcher 객체를 넘기면 됨
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun main() = runBlocking<Unit> { | |
val dispatcher = newSingleThreadContext("Single thread") | |
val multiThreadDispatcher = newFixedThreadPoolContext(2, "MultiThread") | |
launch(dispatcher) { | |
println("[${Thread.currentThread().name}] 실행") | |
} | |
launch(multiThreadDispatcher) { | |
println("[${Thread.currentThread().name}] 실행") | |
} | |
launch(multiThreadDispatcher) { | |
println("[${Thread.currentThread().name}] 실행") | |
} | |
} |

2. 부모 코루틴의 CoroutineDispatcher을 사용해 자식 코루틴 실행
- 코루틴은 구조화를 제공해 코루틴 내부에서 새로운 코루틴 실행 가능
- 바깥쪽의 코루틴을 부모 코루틴(Parent Coroutine)이라고 지칭
- 내부에서 생성되는 새로운 코루틴을 자식 코루틴(Child Coroutine)이라고 지칭
- 자식 코루틴에 CoroutineDispatcher 객체가 설정되지 않았을 경우 부모 코루틴의 CoroutineDispatcher 객체를 사용
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlinx.coroutines.launch | |
import kotlinx.coroutines.newFixedThreadPoolContext | |
import kotlinx.coroutines.runBlocking | |
fun main() = runBlocking<Unit> { | |
val multiThreadDispatcher = newFixedThreadPoolContext(2, "MultiThread") | |
launch(multiThreadDispatcher) { | |
println("[${Thread.currentThread().name}] 부모 코루틴 실행") | |
launch { | |
println("[${Thread.currentThread().name}] 자식 코루틴 실행") | |
} | |
launch { | |
println("[${Thread.currentThread().name}] 자식 코루틴 실행") | |
} | |
} | |
} |

부연 설명
- 자식 코루틴에는 별도 CoroutineDispatcher 객체가 설정돼 있지 않으므로 부모 코루틴에 설정된 CoroutineDispatcher 객체를 사용
- 부모 코루틴과 자식 코루틴이 모두 같은 CoroutineDispatcher 객체를 사용하므로 MultiThread-1과 MultiThread-2를 공용으로 사용하는 것을 볼 수 있음
미리 정의된 CoroutineDispatcher
- newFixedThreadPoolContext 함수를 사용해 직접 CoroutineDispatcher 객체를 생성할 경우 특정 CoroutineDispatcher 객체에서만 사용되는 쓰레드들이 생성되며, 쓰레드 풀에 속한 쓰레드의 수가 너무 적거나 많이 생성되어 비효율적으로 동작할 확률 존재
- 코루틴 라이브러리에서 제공되는 CoroutineDispatcher 객체들은 멀티 쓰레드 프로그래밍이 필요한 일반적인 상황에 맞춰 만들어졌기 때문에 사용자들이 매번 새로운 CoroutineDispatcher 객체를 만들 필요 없이 제공되는 CoroutineDispatcher 객체를 사용해 코루틴을 실행할 수 있도록 미리 정의된 (제한된) CoroutineDispatcher들의 목록을 다음과 같이 제공함
- Dispatchers.IO
- Dispatchers.Default
- Dispatcher.Main
1. Dispatchers.IO
- 멀티 쓰레드 프로그래밍이 가장 많이 사용되는 작업은 I/O 작업
- ex) 애플리케이션 내 네트워크 통신을 위해 HTTP 요청을 하거나 DB 작업 같은 입출력 작업 여러 개를 동시에 수행하기 위해서는 많은 쓰레드 필요
- 코루틴 라이브러리에서는 입출력 작업을 위해 미리 정의된 Dispatchers.IO를 제공
- 코루틴 라이브러리 1.7.2 버전을 기준으로 Dispatchers.IO가 최대로 사용할 수 있는 쓰레드의 수는 {JVM에서 사용이 가능한 프로세서의 수}와 64 중 더 큰 값으로 설정 가능
- Dispatchers.IO는 싱글톤 인스턴스이므로 launch 함수의 인자로 곧바로 넘겨 사용 가능
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun main() = runBlocking<Unit> { | |
launch(Dispatchers.IO) { | |
println("[${Thread.currentThread().name}] 코루틴 실행") | |
} | |
} |

부연 설명
- DefaultDispatcher-worker가 쓰레드명의 접두사로 붙은 쓰레드는 코루틴 라이브러리에서 제공하는 공유 쓰레드 풀에 속한 쓰레드
- Dispatchers.IO와 Dispatchers.Default는 공유 쓰레드 풀의 쓰레드를 사용할 수 있도록 구현되어 있음
2. Dispatchers.Default
- 대용량 데이터를 처리해야 하는 작업처럼 CPU 연산이 필요한 작업도 존재하며 이러한 작업을 CPU 바운드 작업이라고 지칭
- CPU 바운드 작업은 작업을 하는 동안 쓰레드를 지속적으로 사용하므로 쓰레드 기반 작업을 사용해 실행했을 때와 코루틴을 사용해 실행했을 때와 처리 속도면에 큰 차이 없음
- 반면, IO 작업의 경우 작업을 실행한 후 결과를 반환받을 때까지 쓰레드 점유를 하지 않기 때문에 코루틴 사용할 경우 처리 속도면에서 유의미하게 차이가 나는 것을 확인 가능
- Dispatchers.Default는 CPU 바운드 작업이 필요할 때 사용하는 CoroutineDispatcher
- Dispatchers.Default도 그 자체로 싱글톤 인스턴스이므로 Dispatchers.IO와 마찬가지로 launch 함수의 인자로 곧바로 넘겨 사용 가능
2.1 limitedParallelism을 통해 Dispatchers.Default 쓰레드 사용 제한
- Dispatchers.Default를 사용해 무겁고 오래 걸리는 연산을 처리할 경우 특정 연산을 위해 Dispatchers.Default의 모든 쓰레드가 사용될 수도 있으며 이 경우 해당 연산이 모든 쓰레드를 사용하는 동안 Dispatchers.Default를 사용하는 다른 연산이 실행되지 못함
- 이를 방지하기 위해 코루틴 라이브러리는 Dispatchers.Default의 일부 쓰레드만 사용해 특정 연산을 실행할 수 있도록 limitedParallelism 함수 지원
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun main() = runBlocking<Unit> { | |
launch(Dispatchers.Default.limitedParallelism(2)) { | |
repeat(10) { | |
launch { | |
println("[${Thread.currentThread().name}] 코루틴 실행") | |
} | |
} | |
} | |
} |

부연 설명
- Dispatchers.Default의 여러 쓰레드 중 두 개의 쓰레드만 사용해 10개의 코루틴을 실행시키도록 limitedParallelism 적용
3. Dispatchers.Main
- 일반적으로 UI가 있는 애플리케이션에서 메인 쓰레드의 사용을 위해 사용되는 특별한 CoroutineDispatcher 객체
- Dispatchers.IO나 Dispatchers.Default와는 성격이 다름
- 코루틴 라이브러리에 대한 의존성만 추가한다고 해서 Dispatchers.Main을 사용할 수 없고 별도 라이브러리인 `kotlinx-coroutines-android` 등을 추가해야 사용 가능
비고
1. 공유 쓰레드 풀
- 앞서 언급했다시피 Dispatchers.IO와 Dispatchers.Default가 같은 쓰레드 풀인 공유 쓰레드 풀을 사용하므로 코루틴을 실행시킨 쓰레드의 접두사가 DefaultDispatcher-worker인 것을 확인 가능
- 코루틴 라이브러리는 쓰레드의 생성과 관리를 효율적으로 할 수 있도록 애플리케이션 레벨의 공유 쓰레드 풀 제공
- 쓰레드를 무제한으로 생성 가능
- 코루틴 라이브러리는 공유 쓰레드 풀에 쓰레드를 생성하고 사용할 수 있도록 API 제공
- newFixedThreadPoolContext 함수로 만들어진 디스패처가 자신만 사용할 수 있는 전용 쓰레드 풀을 생성하는 것과 다르게 Dispatchers.IO와 Dispatchers.Default는 공유 쓰레드 풀의 쓰레드를 사용

2. Dispatchers.IO의 limitedParallelism
- Dispatchers.Default에서 limitedParllelism 함수를 사용하면 위 사진처럼 Dispatchers.Default가 사용할 수 있는 쓰레드 중 일부만을 사용할 수 있음
- Dispatchers.IO의 limitedParallelism 함수는 공유 쓰레드 풀의 쓰레드로 구성된 새로운 쓰레드 풀을 생성하며, 만들어낼 수 있는 쓰레드에 제한이 있는 Dispatchers.IO나 Dispatchers.Default와 달리 쓰레드의 수를 제한 없이 만들 수 있음
- 특정한 작업이 다른 작업에 영향을 받지 않도록 별도 쓰레드 풀에서 실행되는 것이 필요할 때 사용
- 별도 쓰레드 풀을 생성하는 것은 비싼 작업이므로 남용 X
참고
코틀린 코루틴의 정석 (조세영 저)
반응형
'Kotlin > 코틀린 코루틴의 정석' 카테고리의 다른 글
[Kotlin 코루틴] 예외 처리 (0) | 2024.11.22 |
---|---|
[Kotlin 코루틴] 구조화된 동시성 (0) | 2024.11.21 |
[Kotlin 코루틴] CoroutineContext 정리 (0) | 2024.11.19 |
[Kotlin 코루틴] async와 Deferred (0) | 2024.11.15 |
[Kotlin 코루틴] 코루틴 빌더와 Job 정리 (0) | 2024.11.08 |