-
Kotlin 코루틴(Coroutines)의 Context and Dispatchers🐳Dev/Dev 2022. 7. 16. 17:06
1. Coroutine Context and Dispatchers
1. Dispatchers and Threads
모든 코루틴 빌더들은 선택적으로 CoroutineContext를 매개변수로 가지고 있다.
코루틴은 coroutineContext에서 실행되며, coroutineContext의 요소 중에 Dispatcher가 있다.
dispatcher는 코루틴이 어떤 스레드나 스레드풀에서 실행될지 결정해준다.
import kotlinx.coroutines.* fun main() =runBlocking<Unit>{ launch{ println("main runBlockin : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Unconfined){ println("Unconfined : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Default){ println("Default : I'm working in thread ${Thread.currentThread().name}") } launch(newSingleThreadContext("MyOwnThread")){ println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") } }
- Dispatcher.Default는 DefaultDispatcher-worker-1 쓰레드에서 실행되는데, 이는 GlobalScope에서 실행한 코루틴의 쓰레드와 같다.
- main runBlocking의 경우에는, 자신을 호출한 coroutineScope에서 coroutineContext를 상속받아서 작업되기에 main 쓰레드에서 실행된다.
- newSingleThread의 경우에는, 비용이 많이드는데, 그 이유는 새로운 쓰레드를 만들기 때문이다. 그리고 아래와 같은 방식으로 use를 사용해야, 쓰레드가 close를 통해 정상적으로 종료되어 메모리 누수를 막을 수 있다.
newSingleThreadContext("MyOwnThread").use { launch(it){ println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") } }
2. Debugging corouutines and threads
코루틴은 디버깅이 어렵다! 비동기에 스레드에..
그래서 스레드 이름은 Thread.currentThread().*name* 으로 확인하고, 어떤 코루틴에서 실행된 것인지는 JVM Option에 -Dkotlinx.coroutines.debug 를 설정하여 확인한다! (나는 안돼ㅠ)
위의 코드를 코루틴 디버깅 설정과 함께 실행하면 아래와 같은 결과가 나온다.
3. Jumping between threads
스레드를 스위칭하는 예제를 확인해보자.
import kotlinx.coroutines.* 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") } } } }
위의 예제를 통해 하나의 코루틴이 여러 쓰레드를 스위칭 하는 것을 볼 수 있다.
4. Job in the context
context의 중요한 요소로는 Job이 있다. 아래와 같은 코드로 Job을 받아올 수 있다.
cancel에서 사용된 확장 프로퍼티, isActive도 내부적으로 이 Job이 실행되고 있는 지를 확인한다.
import kotlinx.coroutines.* fun main() = runBlocking<Unit> { println("My job is ${coroutineContext[Job]}") launch { println("My job is ${coroutineContext[Job]}") } async { println("My job is ${coroutineContext[Job]}") } }
5. Children of a coroutine
코루틴 Job들은 위계를 가지고 있고, 코루틴이 실행될 때 부모자식의 관계를 가진다.
코루틴 안에서 생성되는 새로운 코루틴은 부모자식 관계를 가지게 된다. 예외적으로 GlobalScope을 통해 생성된 코루틴은 별도로 Job이 생성된다.
import kotlinx.coroutines.* fun main() =runBlocking<Unit>{ val request =launch{ GlobalScope.launch(){ println("job1: I run in my own Job and execute independently!") delay(1000) println("job1: I am not affected by cancellation of the request") } launch{ delay(100) println("job2: I am a child of the request coroutine") delay(1000) println("job2: I will not execute this line if my parent request is cancelled") } } delay(500) request.cancel() delay(1000) println("main: Who has survived request cancellation?") }
request가 cancel되면서 자식들도 cancel 되지만,
GlobalScope로 만들어진 코루틴은 끝까지 실행되는 것을 볼 수 있다.
6. Children of a coroutine
join을 사용하지 않아도, 부모 코루틴은 자식 코루틴이 끝날 때 까지 기다려줄 책임이 있다.
7. Combining context elements
코루틴 요소들을 합쳐서 하나로 만드는 방법에 대해서 알아보자
+가 오버로딩 되어 있어, 요소들을 하나씩 연결해주면 된다!
fun main() =runBlocking<Unit>{ launch(Dispatchers.Default + CoroutineName("test")){ println("I'm working in thread ${Thread.currentThread().name}") } }
8. Coroutine scope
실행 중이던 앱을 종료해서, 돌고있던 코루틴을 전부 종료해야 하는 상황이 주어졌다!
이때 모든 코루틴을 일일이 cancel하기는 어렵다,
이때 Coroutine scope를 이용하면 하위 코루틴을 전부 cancel할 수 있다!
import kotlinx.coroutines.* class Activity { private val mainScope =CoroutineScope(Dispatchers.Default) fun destroy() { mainScope.cancel() } fun doSomething() { repeat(10){i-> mainScope.launch{ delay((i + 1) * 200L) println("Coroutine $i is done") } } } } fun main() =runBlocking<Unit>{ val activity = Activity() activity.doSomething() println("Launched coroutines") delay(500L) println("Destroying activity!") activity.destroy() delay(1000) }
위의 함수 detroy를 통해 mainScope에서 실행되었던 모든 코루틴이 한번에 종료되는 것을 볼 수있다.
참조
새차원의 코틀린 코루틴(Coroutines) 강좌
새차원의 코틀린 코루틴(Coroutines) 강좌
www.youtube.com
'🐳Dev > Dev' 카테고리의 다른 글
Docker, Image 만들고 Container를 띄워보자 (0) 2022.07.17 Kotlin 코루틴(Coroutines)의 Suspending Functions (0) 2022.07.16 Kotlin 코루틴(Coroutines)의 Cancellation과 Timeouts (0) 2022.07.16 Kotlin 코루틴(Coroutines)의 개념과 기본 사용법 (0) 2022.07.16 PostGIS과 Hibernate-Spatial로 공간 데이터를 다루기 (0) 2022.07.16