-
CoroutineDispatcherBackEnd/coroutine 2025. 5. 6. 08:30반응형
CoroutineDispatcher
- 코루틴을 스레드로 보내 실행시키는 객체
CoroutineDispatcher는 코틀린 코루틴의 핵심 구성요소 중 하나로, 코루틴이 어떤 스레드나 스레드 풀에서 실행될지를 결정하는 역할을 합니다. 즉, "코루틴을 어디에서, 어떻게 실행할 것인가"를 제어하는 실행 컨텍스트의 일부입니다.
SingleThreadDispatcher
SingleThreadDispatcher는 오직 하나의 스레드에서만 코루틴을 실행하도록 보장하는 디스패처(Dispatcher)입니다.
import kotlinx.coroutines.* val singleThreadDispatcher: CoroutineDispatcher = newSingleThreadContext("SingleThread") fun main() = runBlocking<Unit> { launch(singleThreadDispatcher) { println("[${Thread.currentThread().name}] 실행") } }
- launch 함수의 인자로 singleThreadDispather를 넘기면 됩니다.
MultiThreadDispatcher
MultiThreadDispatcher는 여러 개의 스레드(스레드 풀)을 사용하는 디스패처(Dispatcher)입니다. 코루틴이 동시에 여러 스레드에서 병렬로 실행될 수 있도록 관리하는 역할을 합니다.
import kotlinx.coroutines.* val multiThreadDispatcher: CoroutineDispatcher = newFixedThreadPoolContext(2, "MultiThread") // 사용할 스레드 수, 스레드 풀에 속한 각 스레드의 이름 접두사 fun main() = runBlocking<Unit> { launch(multiThreadDispatcher) { println("[${Thread.currentThread().name}] 실행") } launch(multiThreadDispatcher) { println("[${Thread.currentThread().name}] 실행") } }
newFixedThreadPoolContext 함수의 문제
This is a delicate API and its use requires care.
Make sure you fully read and understand documentation of the declaration that is marked as a delicate API.
newFixedThreadPoolContext 함수는 고정된 개수의 스레드 풀을 생성하는 API입니다. 하지만 리소스 누수 위험, 불필요한 스레드 생성, 비효율적인 컨텍스트 스위칭, 스레드 풀 분리로 인한 비효율 등 여러 가지 문제로 인해 API 자체가 "delicate(섬세한)"으로 분류되어, 문서를 충분히 읽고 이해한 후에만 사용해야 한다는 경고 문구가 발생합니다.
코루틴 라이브러리에서 제공하는 코루틴 디스패처
- Dispatchers.IO
- Dispatchers.Default
- Dispatchers.Main
- Dispatchers.Unconfined (무제한 디스패처의 경우, 이후 포스팅에서 다룹니다.)
Dispatchers.IO
네트워크 요청이나 DB 읽기 쓰기 같은 입출력(I/O) 작업을 실행하는 디스패처(Dispatcher)입니다.
- I/O 작업 전용
- 대규모 동시성 지원: 사용할 수 있는 스레드의 수(64와 CPU 코어 수 중 큰 값)
import kotlinx.coroutines.* fun main() = runBlocking<Unit> { launch(Dispatchers.IO) { // 자식 코루틴에 디스패처가 설정되지 않으면, 부모 코루틴에 설정된 디스패처가 사용됩니다. launch { println("[${Thread.currentThread().name}] 작업1 실행") } launch { println("[${Thread.currentThread().name}] 작업2 실행") } launch { println("[${Thread.currentThread().name}] 작업3 실행") } } }
Dispatchers.IO의 limitedParallelism
동시에 실행되는 IO 작업의 개수를 지정한 값만큼 제한하는 독립적인 디스패처 뷰를 만듭니다. 각 뷰는 서로 독립적으로 병렬성 제한을 가집니다. 다른 작업에 방해 받지 않아야 하는 중요 작업이 있을 경우 사용합니다.
val dbDispatcher = Dispatchers.IO.limitedParallelism(10) val fileDispatcher = Dispatchers.IO.limitedParallelism(5) // DB 작업은 동시에 10개, 파일 작업은 동시에 5개까지만 실행 launch(dbDispatcher) { ... } launch(fileDispatcher) { ... }
Dispatchers.Default
코틀린 코루틴에서 기본적으로 사용되는 디스패처(Dispatcher)입니다. 즉, 별도로 디스패처를 지정하지 않은 코루틴은 자동으로 Dispatchers.Default에서 실행됩니다.
- CPU 바운드 작업(이미지, 동영상 처리나 대용량 데이터 변환 같은 끊이지 않고 연산이 필요한 작업)에 최적화
- 스레드 풀 크기: 내부적으로 CPU 코어 수와 동일한 수(최소 2개)의 백그라운드 스레드 풀을 사용
- 비블로킹 작업 권장: 블로킹 I/O(네트워크, 파일 등) 작업이 아닌, 연산 중심의 비블로킹 작업에 사용해야 효율적, 블로킹 작업은 Dispatchers.IO 사용
import kotlinx.coroutines.* fun main() = runBlocking<Unit> { launch(Dispatchers.Default) { launch { println("[${Thread.currentThread().name}] 작업1 실행") } launch { println("[${Thread.currentThread().name}] 작업2 실행") } launch { println("[${Thread.currentThread().name}] 작업3 실행") } } }
Dispatchers.Default의 limitedParallelism
기존 디스패처의 병렬 실행 개수를 최대 n개로 제한하는 뷰를 생성합니다. 해당 뷰는 기존 디스패처의 스레드풀을 공유하지만, 동시에 실행되는 코루틴 수만 제한합니다. 즉, 스레드 수를 직접 제한하는 것이 아니라, 동시에 실행되는 코루틴 개수를 제한합니다. CPU 바운드 작업의 병렬성을 조절할 때 유용하며, 별도의 리소스 해제는 필요 없습니다.
import kotlinx.coroutines.* fun main() = runBlocking<Unit> { val imageProcessingDispatcher = Dispatchers.Default.limitedParallelism(2) repeat(1000) { launch(imageProcessingDispatcher) { Thread.sleep(1000L) // 이미지 처리 작업 println("[${Thread.currentThread().name}] 이미지 처리 완료") } } }
코틀린 라이브러리의 공유 스레드풀
Dispatchers.Default와 Dispatchers.IO는 JVM 프로세스 내에서 공유되는 스레드풀을 사용합니다. 즉, 같은 앱(프로세스) 내에서 여러 코루틴 디스패처가 하나의 스레드풀을 효율적으로 나누어 사용합니다. 공유 스레드풀에서는 스레드를 무제한으로 생성할 수 있고, 스레드는 필요에 따라 자동으로 늘어나거나 줄어드는 탄력적(elastic) 구조를 가지고 있습니다.
Dispatchers.Main
메인 스레드에서의 작업을 위한 디스패처(Dispatcher)입니다. 주로 안드로이드, JavaFX, Swing 등 UI 프레임워크에서 UI 업데이트, 사용자 입력 처리 등 UI 관련 작업을 수행할 때 사용합니다.
기본 코루틴 라이브러리에는 구현체가 없습니다. 사용을 위해서는 안드로이드 코루틴 라이브러리(kotlinx-coroutines-android) 추가가 필요합니다.
import kotlinx.coroutines.* fun main() = runBlocking(Dispatchers.Main) { launch { // 이 코드는 메인(UI) 스레드에서 실행됨 println("Running on Main dispatcher") delay(1000) println("Task completed on Main thread") } }
Dispatchers.Main.immediate
Dispatchers.Main은 어느 스레드에서 코루틴을 실행 요청하든 코루틴을 작업 대기열에 먼저 적재한 후 Main Thread가 비었을 때 코루틴을 보냅니다. Dispatchers.Main.immediate는 코루틴을 실행하는 코드가 메인 스레드에서 실행되고 있다면, 작업 대기열에 적재 없이 바로 메인 스레드에서 실행될 수 있게 합니다. 만약 백그라운드 스레드에서 코루틴을 실행 요청하면, 일반 Main과 동일하게 작업 대기열에 적재 후 메인 스레드로 보냅니다.
반응형'BackEnd > coroutine' 카테고리의 다른 글
Coroutine 순차 처리 (0) 2025.05.09 runBlocking (0) 2025.05.05 Coroutine 등장 배경 (0) 2025.05.03