Coroutines guide
코루틴 공식 가이드 문서를 읽고 요약 정리해보려 한다.
목표는 Kotlin Coroutine을 완벽하게 이해하는 것!!
더불어, 영어로 된 가이드 문서를 읽는 연습도 할 것이다! : )
Coroutines basics
코루틴은 중단가능한 계산의 인스턴스이다.
스레드와 비슷한 개념이지만, 코루틴은 특정 스레드에 바인딩되지 않는다.
하나의 스레드에서 실행을 중지하고, 다른 스레드에서 다시 실행할 수 있다.
코드
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
}
출력
Hello
World!
위 코드를 분석해보자.
launch
코루틴 빌더이다. 나머지 코드와 새로운 코루틴을 동시에 시작하며 독립적으로 동작한다.
CoroutineScope에서만 선언될 수 있다.
Hello가 먼저 출력될 수 있었던 이유가 launch가 아래 println("Hello")
와 동시에 실행될 수 있었기 때문이다.
delay
suspend 함수. 중단함수이다. 특정 시간동안 코루틴을 중지시킬 수 있다.
코루틴을 중지시키면 기본 스레드는 차단되지 않는다. 그 동안 다른 코루틴이 기본 스레드를 사용할 수 있도록 한다.
runBlocking
lauch
와 같이 코루틴 빌더이다. 코루틴이 아닌 fun main()
과 runBlocking { ... }
내부의 코루틴 코드를 연결해준다.
만약 위 코드에 runBlocking
이 없었다면, launch
호출 시 에러가 발생한다. launch는 CoroutineScope에서만 선언될 수 있기 때문이다.
호출 시 내부에 있는 코루틴의 실행이 끝날 때 까지 스레드가 차단된다.
비싼 리소스이고, 비효율적이므로 거의 사용되지 않는다.
Structured concurrency
코루틴은 구조화된 동시성의 원칙을 따른다.
새로운 코루틴은 코루틴의 수명을 제한하는 특정 CoroutineScope에서만 실행될 수 있다.
외부 영역은 자식 코루틴이 끝날 때까지 종료될 수 없다.
손실이나 누수를 방지한다.
Extract function refactoring
launch { ... }
내부의 코드를 별도의 함수로 분리하려면 새로운 함수 앞에 suspend
수정자를 붙이면 된다.
코드
fun main() = runBlocking { // this: CoroutineScope
launch { doWorld() }
println("Hello")
}
// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")
}
Scope builder
CoroutineScope
빌더를 사용해서 코루틴 범위를 지정할 수 있다.
CoroutineScope 범위를 생성하고, 실행된 자식 코루틴이 끝날 때까지 종료되지 않는다.
runBlocking vs coroutineScope builder
공통점
- 범위 내 하위 코루틴이 모두 끝날 때까지 기다린다.
차이점
- runBlocking
- 기다리기 위해 현재 스레드를 차단한다.
- 일반 함수
- coroutineScope
- 일시 중지한 뒤, 해당 스레드는 다른 용도로 사용한다.
- 중단 함수
코드 1
fun main() = runBlocking {
doWorld()
}
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
출력
Hello
World!
코루틴 스코프 빌더내에서 launch { … }
내부의 delay로 인해 1초 동안 중지되어 있는 동안 스레드는 Hello를 출력한다. 1초의 대기 후에 world를 출력하게 된다.
코드 2
fun main() {
runBlocking {
delay(1000L)
println("World!")
}
println("Hello")
}
출력
World!
Hello
runBlocking
은 스레드를 차단하므로 내부 코루틴이 모두 실행될 때까지 Hello가 먼저 출력될 수 없다.
Scope builder and concurrency
코루틴스코프 필더는 suspend 함수 내에서 여러개의 동시 작업을 할 때 사용할 수 있다.
코드
// Sequentially executes doWorld followed by "Done"
fun main() = runBlocking {
doWorld()
println("Done")
}
// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(2000L)
println("World 2")
}
launch {
delay(1000L)
println("World 1")
}
println("Hello")
}
출력
Hello
World 1
World 2
Done
기다리는 시간동안 스레드가 다른 동작을 할 수 있으므로 여러개의 자식 코루틴이 동시에 실행될 수 있다.
코루틴스코프는 내부의 모든 하위 코루틴이 완료될 때까지 기다렸다가 종료한다.
An explicit job
launch
코루틴 빌더는 Job
객체를 반환한다.
실행된 코루틴이 완료될 때까지 명시적으로 기다리는데 사용할 수 있다.
코드
val job = launch { // launch a new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello")
job.join() // wait until child coroutine completes
println("Done")
출력
Hello
World!
Done
join
을 사용하면 해당 코루틴이 완료될 때까지 기다린다.
Coroutines are light-weight
코루틴은 JVM스레드보다 리소스 집약적이지 않다.
코루틴 대신 스레드만을 사용해서 구현한다면 많은 JVM 메모리를 소비하게 된다.
코루틴을 사용하면 리소스 제한에 도달하지 않고 표현할 수 있다.
'Android > Coroutine' 카테고리의 다른 글
[Kotlin/coroutine] 5. Asynchronous Flow (1) (0) | 2023.10.25 |
---|---|
[Kotlin/Coroutine] 4. Coroutine context and dispatchers (0) | 2023.10.24 |
[Kotlin/Coroutine] 3. Composing suspending functions (2) | 2023.10.23 |
[Kotlin/Coroutine] 2. Cancellation and timeouts (0) | 2023.10.22 |