CoroutineContext
임Dispatchers.Default
Dispatchers.Default
임
runBlocking
은 디스패처 설정이 없으면 자신만의 디스패처를 사용함. =main
스레드
limitedParallelism
private val = dispatcher = Dispatchers.Default.limitedParallelism(5)
kotlinx.coroutines
1.6 에서 도입됨
Dispatchers.Main
Dispatchers.IO
Dispatchers.Default
의 스레드 수는 프로세서가 가지고 있는 코어 수로 제한되지만, Dispatchers.IO
는
IO
디스패처는 Default
디스패처와 스레드 풀을 공유한다.
IO
디스패처 자체 한도로 적용된다. 스레드 한도는 독립적이기 때문에 다른 디스패처의 스레드 고갈 시키지 않음class DiscUserRepository(
private val discReader: DiscReader
) : UserRepostiroy {
override suspend fun getUser(): UserData = withContext(Dispatchers.IO) {
UserData(discREader.read("userName"))
}
}
withContext(Dispatchers.IO)
로 래핑한 함수가 너무 많은 스레드를 블로킹하면 문제가 될 수도 있음
limitedParallelism
사용할 수 있음limitedParallelism
을 Default 디스패처에 사용할 경우 스레드 수 제한을 추가하는 것Dispatchers.IO
독립적인 디스패처를 만드는 것val dispatcher = Dispatchers.IO.limitedParallelism(100)
limitedParallelism
을 가장 잘 사용하는 방법은 스레드를 블로킹하는 경우가 잦은 클래스에서 별도 한도를 가지는 커스텀 디스패처를 정의해서 쓰는 것
Dispatchers.IO
를 비롯한 다른 디스패처와 무관하기 때문에 한 서비스가 다른 서비스를 블로킹 하는 경우가 없음위에서 언급했듯이
limitedParallelism
이kotlinx.coroutines
1.6 에 추가되었고, 아래 방식은 이전에 쓰던 방식인 듯
ExecutorService
, Executor
사용하여 별도의 스레드 풀을 만들고 디스패처에 먹이던 방식val dispatcher = Executors
.newFixedThreadPool(20)
.asCoroutineDispatcher()
close
함수로 직접 닫아야함Dispatchers.Default.limitedParallelism(1)
Dispatchers.IO.limitedParallelism(1) // 스레드 블로킹이 되는 경우
val dispatcher = Executors
.newVirtualThreadPerTaskExecutor()
.asCoroutineDispatcher()
// or 객체로 만들고 Dispatchers.Loom 으로 확장
object LoomDispatcher : ExecutorCoroutineDispatcher() {
// ... p152
}
val Dispatchers.Loom: CoroutineDispatcher
get() = LoomDispatcher
이 책의 원서 출판 시점이 2022년이었고, 현재 시점에서 Java 21 과 함께 긍정적으로 도입 검토해볼 필요는 있을 듯. 우리는 대부분 블로킹 API 호출이 많으니깐
Dispatchers.Unconfined
"Dispatchers.Unconfined 는 어떤 상황에서 쓸 수 있나요?" by ChatGPT
Dispatchers.Unconfined
는 코틀린 코루틴에서 제공하는 디스패처 중 하나로, 매우 독특한 동작 방식을 가지고 있습니다. 이 디스패처는 다른 디스패처들과 달리 특정 스레드 풀이나 스레드에 바인딩되지 않고, 코루틴을 시작한 스레드에서 실행을 시작한 후, 첫 번째 서스펜드 이후에는 호출된 스레드에서 계속 실행됩니다. 이 때문에Dispatchers.Unconfined
는 특정한 상황에서만 사용하는 것이 좋습니다.¶
Dispatchers.Unconfined
의 동작 방식
코루틴 시작:
- 코루틴이
Dispatchers.Unconfined
디스패처에서 시작되면, 현재 스레드에서 실행됩니다. 이 스레드는main
스레드일 수도 있고, 백그라운드 스레드일 수도 있습니다.첫 번째 서스펜드 이후:
- 코루틴이 첫 번째 서스펜드 지점에 도달하면, 해당 시점에서 스레드가 변경될 수 있습니다. 이후 실행은 재개된 컨텍스트에서 진행됩니다. 이로 인해 코루틴이 예상치 못한 스레드에서 실행될 수 있습니다.
¶ 사용 사례
Dispatchers.Unconfined
는 특별한 경우에 유용할 수 있습니다:
작업의 컨텍스트가 중요하지 않을 때:
- 코루틴이 특정 스레드에서 실행되어야 하는 요구가 없고, 단순히 실행 흐름만 유지하고 싶을 때 사용할 수 있습니다. 예를 들어, 간단한 데이터 처리나 메인 스레드에서 실행할 필요가 없는 로직에 사용할 수 있습니다.
UI 업데이트와 같은 서스펜드 포인트가 없는 작업:
- UI 스레드에서 UI 업데이트와 같은 간단한 작업을 수행할 때,
Dispatchers.Unconfined
를 사용하여 작업이 서스펜드 포인트 없이 빠르게 완료되도록 할 수 있습니다. 다만, UI 작업이 복잡해지거나 서스펜드 포인트가 생기면 이 디스패처를 사용하는 것이 적절하지 않을 수 있습니다.성능 측정:
- 특정 상황에서 코루틴의 성능을 측정할 때, 스레드 전환의 영향을 피하고 싶을 경우 사용할 수 있습니다. 이 경우 스레드 스위칭 없이 코루틴이 가능한 한 빨리 실행되도록 할 수 있습니다.
¶ 사용하지 말아야 할 경우
Dispatchers.Unconfined
를 사용하지 말아야 하는 상황도 있습니다:
UI 작업:
- UI 작업에서
Dispatchers.Unconfined
를 사용할 경우, 서스펜드 이후에 UI 스레드 외부에서 코루틴이 재개될 수 있어 UI 업데이트가 예기치 않게 동작할 수 있습니다. 이로 인해 앱이 충돌하거나 예기치 않은 동작이 발생할 수 있습니다.스레드 안전성이 중요한 작업:
- 여러 스레드에서 안전하게 작업해야 하는 경우에는
Dispatchers.Unconfined
를 사용하면 예상치 못한 스레드에서 코드가 실행되어 데이터 경쟁 조건이나 동시성 문제가 발생할 수 있습니다.복잡한 비동기 작업:
- 비동기 작업이나 네트워크 요청, 파일 I/O 등에서
Dispatchers.Unconfined
를 사용하는 것은 권장되지 않습니다. 이러한 작업에서는 특정 스레드에서 실행을 보장할 수 있는 다른 디스패처(Dispatchers.IO
등)를 사용하는 것이 안전합니다.¶ 결론
Dispatchers.Unconfined
는 특정 스레드에 바인딩되지 않고, 현재 스레드에서 코루틴을 실행하기 때문에, 주로 간단한 작업이나 스레드 전환이 필요하지 않은 상황에서 사용할 수 있습니다. 하지만 스레드 전환이나 안전성이 중요한 작업에서는 사용을 피하고, 적절한 디스패처(예:Dispatchers.Main
,Dispatchers.IO
,Dispatchers.Default
)를 사용하는 것이 좋습니다.
Dispatchers.Main
에서만 지원함중단 | 블로킹 | CPU 집약적인 연산 | 메모리 집약적인 연산 | |
---|---|---|---|---|
싱글스레드 | 1,002 | 100,003 | 39,103 | 94,358 |
디폴트 디스패처(스레드 8개) | 1,002 | 13,003 | 8,473 | 21,461 |
IO 디스패처(스레드 64개) | 1,002 | 2,003 | 9,893 | 20,776 |
스레드 100개 | 1,002 | 1,003 | 16,379 | 21,004 |
Dispatchers.Default
가 제일 좋음