CoroutineContext
코루틴은 Kotlin 표준 라이브러리에 정의된 CoroutineContext 유형의 값으로, 코루틴을 어떻게 처리할 지에 대한 집합이고 유형의 값에 따른 Context에서 실행된다.
CoroutineContext는 다양한 요소들의 집합이고 Job, Dispatchers를 가진다.
CoroutineContext Method
☞ get() : 연산자(operator) 함수로써 주어진 key 에 해당하는 컨텍스트 요소를 반환합니다.
☞ fold() : 초기값(initialValue)을 시작으로 제공된 병합 함수를 이용하여 대상 컨텍스트 요소들을 병합한 후 결과를 반환합니다.
예를들어 초기값을 0을 주고 특정 컨텍스트 요소들만 찾는 병합 함수(filter 역할)를 주면 찾은 개수를 반환할 수 있고, 초기값을 EmptyCoroutineContext 를 주고 특정 컨텍스트 요소들만 찾아 추가하는 함수를 주면 해당 요소들만드로 구성된 코루틴 컨텍스트를 만들 수 있습니다.
☞ plus() : 현재 컨텍스트와 파라미터로 주어진 다른 컨텍스트가 갖는 요소들을 모두 포함하는 컨텍스트를 반환합니다. 현재 컨텍스트 요소 중 파라미터로 주어진 요소에 이미 존재하는 요소(중복)는 버려집니다.
☞ minusKey() : 현재 컨텍스트에서 주어진 키를 갖는 요소들을 제외한 새로운 컨텍스트를 반환합니다.
요약하자면 코루틴 컨텍스트(CoroutineContext)에는 코루틴 컨텍스트를 상속한 요소(Element) 들이 등록될 수 있고, 각 요소들이 등록 될때는 요소의 고유한 키를 기반으로 등록된다는 것입니다.
Dispatcher 및 Thread
CoroutineContext에는 해당 코루틴이 실행을 위해 Thread를 결정하는 CoroutineDispatcher가 포함되는데, 이 것은 코루틴 실행을 특정 Thread로 제한시키거나, Thread pool로 급파하거나 제한없이 실행되도록 할 수 있다.
launch 및 async는 CoroutineScope의 확장함수로, 넘겨받은 Coroutine 블럭에 대해 Coroutine을 만들어 실행해주는 빌더이며, 새 코루틴 및 기타 컨텍스트 요소에 대한 Dispatcher를 명시적으로 사용할 수 있도록 CoroutineContext 매개 변수를 허용한다.
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
// sampleStart
launch { // 부모의 컨텍스트, 기본 runBlocking 코 루틴
println("main runBlocking : ${Thread.currentThread().name} 스레드에서 작업 중")
}
launch(Dispatchers.Unconfined) { // 제한되지 않음-주 스레드와 함께 작동합니다
println("Unconfined : ${Thread.currentThread().name} 스레드에서 작업 중")
}
launch(Dispatchers.Default) { // DefaultDispatcher로 발송됩니다.
println("Default : ${Thread.currentThread().name} 스레드에서 작업 중")
}
launch(newSingleThreadContext(" MyOwnThread ")) { // 자체 새 스레드를 가져옵니다.
println("newSingleThreadContext : ${Thread.currentThread().name} 스레드에서 작업 중")
}
// sampleEnd
}
실행결과:
Unconfined : main @coroutine#3 스레드에서 작업 중
Default : DefaultDispatcher-worker-2 @coroutine#4 스레드에서 작업 중
newSingleThreadContext : MyOwnThread @coroutine#5 스레드에서 작업 중
main runBlocking : main @coroutine#2 스레드에서 작업 중
위 코드처럼 Coroutine Builder에 CoroutineContext 매개변수를 통하여 코루틴의 실행을 스레드 별로 나누어 작업할 수 있다.
launch{} 코루틴 빌더가 파라미터 없이 사용될 때, 메인 쓰레드에서 운행되는 runBlocking coroutine의 context를 상속받으며, CoroutineScope로 부터 Context를 상속받고, 따라서 Dispatcher도 받는다.
newSingleThreadContext는 코루틴이 실행할 스레드를 생성하며, 전용 스레드는 리소스를 많이 차지한다. 실제 앱에서 더 이상 필요하지 않을 때 close() 메서드를 이용하여 해제하거나, 최상위 변수에 저장하여 앱 전체에서 재사용해야 한다.
코루틴이 GlobalScope에서 시작되는 경우엔 기본 Dispatcher는 Dispatchers.Default이며, 스레드의 공유 백 그라운드 풀을 사용하므로 luanch(Dispatchers.Default){}는 GlobalScope.launch{}와 동일한 Dispatcher를 사용한다.
Unconfined Dispatcher 및 confined Dispatcher
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
// sampleStart
launch(Dispatchers.Unconfined) { // 제한되지 않음-주 스레드와 함께 작동합니다
println(" Unconfined : ${Thread.currentThread().name} 스레드")
delay(500)
println(" Unconfined : ${Thread.currentThread().name} 스레드")
}
launch { // 부모의 컨텍스트, 메인 runBlocking 코 루틴
println(" main runBlocking : ${Thread.currentThread().name} 스레드")
delay(1000)
println(" main runBlocking : ${Thread.currentThread().name} 스레드")
}
// sampleEnd
}
실행결과:
Unconfined : main @coroutine#2 스레드
main runBlocking : main @coroutine#3 스레드
Unconfined : kotlinx.coroutines.DefaultExecutor @coroutine#2 스레드
main runBlocking : main @coroutine#3 스레드
Dispatchers.Unconfined Dispatcher는 코루틴을 첫 번째 정지 시점까지만 호출한 스레드에서 시작된다. suspending function(정지 기능)에 의해 정지된 후에는 완전히 결정된 스레드에서 코루틴을 다시 시작한다. Dispatchers.Unconfined는 CPU 시간을 소비하거나, 특정 스레드에서 제한된 공유 데이터(예: UI)를 업데이트하지 않는 코루틴에서 사용하는 것이 적합하다.
반면에 Dispatcher는 기본적으로 CoroutineScope로 부터 상속받으며, runBlocking 코루틴에 대한 기본 Dispatcher는 호출한 스레드로 제한되어 있기 때문에 이를 상속하게되면 예측 가능한 FIFO 스케줄링으로 이 스레드에 실행을 제한하는 효과가 있다.
'Coroutine' 카테고리의 다른 글
Coroutines - Part. 8 Asynchronous Flow (0) | 2021.02.05 |
---|---|
Coroutines - Part. 7 Debugging Coroutine and Thread (0) | 2021.01.30 |
Coroutines - Part. 5 안드로이드에서의 코루틴 - 동기식과 비동기식, 콜백과 코루틴 (0) | 2021.01.30 |
Coroutines - Part. 4 안드로이드에서 코루틴 - ViewModel, Activity, Fragment (0) | 2021.01.30 |
Coroutines - Part. 3 withTimeout(), withTimeoutOrNull() (0) | 2021.01.30 |