728x90
코루틴은 항상 Kotlin 표준 라이브러리에 정의된 CoroutineContext로 대표되는 어떤 context에서 실행되고 코루틴의 context는 여러 요소의 set으로 구성됨
Context
- 어떤 쓰레드에서 코루틴을 실행할지에 대한 Dispatcher의 정보를 담고 있는 그룹임, 스레드 풀을 전환하고, 지정하고, 예외를 잡는데 사용
- Job, Deferred, Dispatcher, CoroutineName, CoroutineExceptionHandler는 모두 CorountineContext 인터페이스에 간접적으로 상속됨
Dispatcher
- 코루틴을 생성하여 해당 코루틴을 Dispatcher에 전송시, Dispatcher는 자신이 관리하는 스레드풀 내의 스레드 부하 상황에 맞춰서 코루틴을 분배함
- 결론적으로 Dispatcher는 코루틴을 특정스레드에서 실행할 수 있도록 도와주는 역할
- 이러한 역할로 인하여 Dispatcher는 Context의 주요 요소 중 하나임
Dispatcher 과정
- 유저가 코루틴을 생성 후 Dispatcher로 전송
- Dispatcher는 자신이 물고 있는 스레드풀에서 자원이 남는 스레드를 확인 후 해당 스레드로 코루틴 전송
- 코루틴 할당 받은 스레드는 수행함
Dispatcher 종류
종류 | 설명 |
Dispatchers.IO | 코루틴이 공유된 스레드 풀에 있는 백그라운드 스레드에서 작동 로컬 DB와 작업 시, 네트워크 작업 or 파일과 관련된 작업할 때 사용 필요에 따라서 스레드를 더생성하거나 줄일 수있고 최대 64개까지 생성이 가능 Default Dispatcher와 스레드를 공유하기 때문에 스위칭으로 인한 오버헤드를 발생시키지 않음 |
Dispatchers.Main | 코루틴이 메인스레드에서 실행 ui / suspending 함수 등 LiveData에서 수정사항을 가져오는 작고 가벼운 작업을 실행 |
Dispatchers.Default | 많은 리스트를 정렬하는 작업과 같이 CPU부하가 많은 작업들을 할 때 사용 |
Dispatchers.Unconfined | GlobalScope와 함께 사용 코루틴이 현재스레드에 작동하고 중단되고 다시 시작되면 중단된 스레드에서 시작 |
- 비동기 백그라운드 작업을 수행 시 가장 많이 사용되는 것은 IO하고 Default임
- Default의 경우 코어수 만큼의 스레드만 생성하여 작업하기 때문에 CPU를 많이 점유하는 작업에서 최대효율을 냄
Dispatcher and threads
fun main() = runBlocking<Unit> {
launch { // 부모의 context. main runblocking context입니다.
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // not confined -- 메인 스레드에서 동작합니다.
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // DefaultDispatcher로 작업이 보내집니다.
println("Default : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // 새로운 스레드로 작업이 전달됩니다.
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}
}
- 메인스레드에서 실행되는 main의 runBlocking 코루틴의 context를받음
- Unconfined는 메인 스레드에서 실행되는 것 처럼 보이지만 아님
- GlobalScope에서는 코루틴이 실행될때 기본적으로 Dispatcher는 default나 혹은 백그라운드 스레드풀에서 공유해서 사용
- newSingleThreadContext는 새로운 스레드를 생성
Unconfined vs confined dispatcher
fun main() = runBlocking<Unit> {
launch(Dispatchers.Unconfined) {
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
delay(500)
println("Unconfined : After delay in thread ${Thread.currentThread().name}")
}
launch {
println("main runBlocking: I'm working in thread ${Thread.currentThread().name}")
delay(1000)
println("main runBlocking: After delay in thread ${Thread.currentThread().name}")
}
}
- Unconfined dispatcher는 호출한 스레드에서 코루틴을 시작하지만 첫번째 suspension까지만 시작함
- suspension이 끝나면 호출된 suspension 함수에 의해 다른 스레드로 코루틴을 재개
- CPU시간을 소비하지 않고 특정스레드에서 공유데이터를 업데이트하지도 않는 코루틴에서는 unconfined가 적합ㅎ마
- confined dispatcher는 기본적으로 외부 코루틴스코프에 dispatcher를 상속받음
Jumping between threads
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() {
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
runBlocking(ctx1) {
log("Started in ctx1")
withContext(ctx2) {
log("Working in ctx2")
}
log("Back to ctx1")
}
}
}
}
- 코루틴의 context를 변경해주는 withContext 함수를 사용
'KOTLIN' 카테고리의 다른 글
[KOTLIN] NULLABLE UNIT 을 리턴하지 말라 (이펙티브코틀린) (0) | 2022.07.03 |
---|---|
[KOTLIN] 변수의 스코프 최소화 (이펙티브코틀린) (0) | 2022.06.18 |
[KOTLIN] CANCELLATION (취소) (0) | 2022.06.09 |
[KOTLIN] SUSPEND 함수 (0) | 2022.06.06 |
[KOTLIN] 코루틴 (0) | 2022.06.01 |