Coroutine이란?
- Coroutine은 일시정지 연산을 위한 인스턴스이다. 이것은 코드의 나머지 부분들과 동시에 실행되는 코드 블록을 가진다는 점에서 스레드와 개념적으로 비슷하다. 하지만, 코루틴은 특정한 스레드에 종속되어 실행되지 않으며, 하나의 스레드에서 일시정지(suspend) 되었다가 다른 스레드에서 재개(resume)될 수 있다.
- 일반적인 스레드와 달리 코루틴은 작은 비용으로 생성되고, 필요할 때만 작업을 실행하거나 중단할 수 있기 때문에, 리소스 효율성이 매우 뛰어나다.
- 코루틴의 장점으로는 간결한 문법과 취소, 중단, 시간 초과에 대한 제어가 유연하다.
Coroutine 핵심 구성 요소
- 중단 가능한 suspend 함수 : Coroutine 안에서만 호출 가능
suspend fun fetchData(): String {
delay(1000) // 1초 대기 (비동기적으로)
return "Hello Coroutine"
}
- Coroutine Builder : 코루틴을 시작하는 함수들
// launch 예제
GlobalScope.launch {
val data = fetchData()
println(data)
}
// async 예제
val deferred = GlobalScope.async {
fetchData()
}
println("result: ${deferred.await()}")
- Coroutine Scope : 코루틴이 실행되는 범위를 관리, ViewModel이나 Lifecycle에 따라 자동으로 취소 가능
viewModelScope.launch {
// lifecycle-aware coroutine
}
- Coroutine Dispatcher
CoroutineScope(Dispatchers.IO).launch {
val result = fetchData() //API, DB 호출
withContext(Dispatchers.Main) {
// UI 업데이트
}
}
- Dispatchers.Main : UI 작업
- Dispatchers.IO : 네트워크, 파일 입출력
- Dispatchers.Default : CPU 연산
- Dispatchers.Unconfined : 호출한 스레드에 의존
Coroutine 생성
fun main() = runBlocking { // this: CoroutineScope
launch { // 새로운 coroutine을 실행하고 계속한다.
delay(1000L) // 블로킹 하지 않고 1초를 지연시킨다. (기본 시간 단위 : ms)
println("World!") // 지연 이후에 프린트한다.
}
println("Hello") // 이전 coroutine이 지연된 동안 main coroutine이 실행된다.
}
- launch는 Coroutine Builder이다. launch는 독립적으로 동작하는 새로운 Coroutine을 나머지 코드와 동시에 실행되도록 한다. 이로 인해 Hello가 처음에 출력된다.
- delay는 특별한 일시중단 함수이다. delay는 Coroutine을 특정한 시간 동안 일시중단한다. Coroutine을 일시중단 하는 것은 Coroutine이 실행 중인 스레드를 블록 하지 않으며, 다른 Coroutine 이 해당 스레드에서 자신들의 코드를 실행할 수 있도록 한다.
- runBlocking 또한 Coroutine Builder이다. runBlocking은 Coroutine 세계에 속하지 않은 일반적인 fun main() 과 runBlocking {... } 중괄호 내부의 Coroutine 코드를 연결시켜 주는 역할을 한다. 이는 IDE 내에서 runBlocking 시작 중괄호 '{' 바로 다음에 오는 this: CoroutineScope 힌트로 강조표시된다.
구조화된 동시성
- Coroutine은 새로운 Coroutine의 수명을 제한하는 특정한 CoroutineScope 내에서만 실행될 수 있다는 원칙인 구조화된 동시성의 원칙을 따른다. 생성에서 나온 예시에서는 runBlocking이 해당 Scope을 만들며, 그것이 이전 예시가 World! 가 프린트 될 때까지 1초를 기다린 후 종료되는 이유이다.
- 구조화된 동시성은 Coroutine들이 손실되거나 누수를 일으키지 않도록 한다. 바깥 Scope은 자식 Coroutine들이 모두 완료될 때까지 완료되지 못한다.
- 또한 구조화된 동시성은 코드 상의 에러가 적절히 보고되고 손실되지 않도록 보장한다.
- 다음에는 구조화된 동시성을 코드로 살펴보도록 하겠다.
Coroutine이란?
- Coroutine은 일시정지 연산을 위한 인스턴스이다. 이것은 코드의 나머지 부분들과 동시에 실행되는 코드 블록을 가진다는 점에서 스레드와 개념적으로 비슷하다. 하지만, 코루틴은 특정한 스레드에 종속되어 실행되지 않으며, 하나의 스레드에서 일시정지(suspend) 되었다가 다른 스레드에서 재개(resume)될 수 있다.
- 일반적인 스레드와 달리 코루틴은 작은 비용으로 생성되고, 필요할 때만 작업을 실행하거나 중단할 수 있기 때문에, 리소스 효율성이 매우 뛰어나다.
- 코루틴의 장점으로는 간결한 문법과 취소, 중단, 시간 초과에 대한 제어가 유연하다.
Coroutine 핵심 구성 요소
- 중단 가능한 suspend 함수 : Coroutine 안에서만 호출 가능
suspend fun fetchData(): String {
delay(1000) // 1초 대기 (비동기적으로)
return "Hello Coroutine"
}
- Coroutine Builder : 코루틴을 시작하는 함수들
// launch 예제
GlobalScope.launch {
val data = fetchData()
println(data)
}
// async 예제
val deferred = GlobalScope.async {
fetchData()
}
println("result: ${deferred.await()}")
- Coroutine Scope : 코루틴이 실행되는 범위를 관리, ViewModel이나 Lifecycle에 따라 자동으로 취소 가능
viewModelScope.launch {
// lifecycle-aware coroutine
}
- Coroutine Dispatcher
CoroutineScope(Dispatchers.IO).launch {
val result = fetchData() //API, DB 호출
withContext(Dispatchers.Main) {
// UI 업데이트
}
}
- Dispatchers.Main : UI 작업
- Dispatchers.IO : 네트워크, 파일 입출력
- Dispatchers.Default : CPU 연산
- Dispatchers.Unconfined : 호출한 스레드에 의존
Coroutine 생성
fun main() = runBlocking { // this: CoroutineScope
launch { // 새로운 coroutine을 실행하고 계속한다.
delay(1000L) // 블로킹 하지 않고 1초를 지연시킨다. (기본 시간 단위 : ms)
println("World!") // 지연 이후에 프린트한다.
}
println("Hello") // 이전 coroutine이 지연된 동안 main coroutine이 실행된다.
}
- launch는 Coroutine Builder이다. launch는 독립적으로 동작하는 새로운 Coroutine을 나머지 코드와 동시에 실행되도록 한다. 이로 인해 Hello가 처음에 출력된다.
- delay는 특별한 일시중단 함수이다. delay는 Coroutine을 특정한 시간 동안 일시중단한다. Coroutine을 일시중단 하는 것은 Coroutine이 실행 중인 스레드를 블록 하지 않으며, 다른 Coroutine 이 해당 스레드에서 자신들의 코드를 실행할 수 있도록 한다.
- runBlocking 또한 Coroutine Builder이다. runBlocking은 Coroutine 세계에 속하지 않은 일반적인 fun main() 과 runBlocking {... } 중괄호 내부의 Coroutine 코드를 연결시켜 주는 역할을 한다. 이는 IDE 내에서 runBlocking 시작 중괄호 '{' 바로 다음에 오는 this: CoroutineScope 힌트로 강조표시된다.
구조화된 동시성
- Coroutine은 새로운 Coroutine의 수명을 제한하는 특정한 CoroutineScope 내에서만 실행될 수 있다는 원칙인 구조화된 동시성의 원칙을 따른다. 생성에서 나온 예시에서는 runBlocking이 해당 Scope을 만들며, 그것이 이전 예시가 World! 가 프린트 될 때까지 1초를 기다린 후 종료되는 이유이다.
- 구조화된 동시성은 Coroutine들이 손실되거나 누수를 일으키지 않도록 한다. 바깥 Scope은 자식 Coroutine들이 모두 완료될 때까지 완료되지 못한다.
- 또한 구조화된 동시성은 코드 상의 에러가 적절히 보고되고 손실되지 않도록 보장한다.
- 다음에는 구조화된 동시성을 코드로 살펴보도록 하겠다.