https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
코루틴의 개념의 첫 구현체는 Simula (1967)
Android 에서 코루틴의 역할은 뭘까?
1. Main thread 를 블럭하는 긴 태스크 (long-running task) 처리
2. Main-safety 는 어떤 suspend function 이든 main thread 에서 호출될 수 있도록 함
suspend 키워드는 코틀린 컴파일러에 특수한 처리를 가하기 위한 용도.
suspend function 은 일반 함수와 달리 두 가지 연산이 더 있음.
일반 함수에서 제공하는 invoke (or call), return + suspend, resume
suspend - 현재 코루틴의 실행을 멈추고, 지역 변수들을 저장함
resume - 지연된 코루틴을 멈췄던 곳에서부터 재개함
suspend, resume 은 콜백을 대체하기 위해 함께 사용된다.
콜백을 어떻게 대체하는가?
suspend fun fetchDocs() {
val docs = get("...") // get 은 suspend 함수, Dispatchers.IO 사용
show(docs)
}
메인 스레드 스택 구조 - 위 GIF 를 순차적으로 따라가면서 어떤 연산을 수행하는지 확인해보자.
1. suspend 키워드가 부착된 fetchDocs 함수가 스택에 올라간다 (invoke)
2. suspend 이므로 스택에서 잠시 제거된 후 재개를 기다린다 (suspend) - 스택에서 제거된다는 표현 보다는, 현재 스택 프레임이 복사된 채로 어딘가에 저장됨.
3. get 이 스택에 올라간다 (invoke)
4. get 또한 suspend 이므로 잠시 스택에서 제거된 후 재개를 기다린다 (suspend)
--- 메인 스레드 위에 있는 모든 코루틴들이 suspend 상태일때 메인 스레드는 free 하다
5. get 작업이 끝난 후 메인 스택에 다시 올라간다 (resume) - 복사했던 스택 프레임이 다시 올라감
6. get 의 결과값이 반환되고 스택에서 제거된다 (return)
7. fetchDocs 가 스택에 다시 올라가고 실행을 재개한다 (resume)
8. show(docs) 까지 실행을 마친 후 스택에서 제거된다 (return)
코루틴은 메인 스레드에서 실행되며, suspend 는 백그라운드 실행을 의미하지 않는다.
코틀린에서는 모든 코루틴이 반드시 Dispatcher 위에서 실행되어야 한다. - main thread 에서 실행 되더라도 마찬가지다.
코루틴은 자기 자신을 suspend 할 수 있다.
Dispatcher 가 바로 suspend 된 코루틴을 resume 하는 방법을 아는 애다.
코루틴이 어디서 실행되어야 할지 명시하기 위해 코틀린은 세 가지 Dispatcher 를 제공한다.
1. Dispatchers.Main
- suspend function 을 호출할때
- UI 함수를 호출할 때
- LiveData 를 업데이트 할 때
2. Dispatchers.IO
디스크, 네트워크 요청
3. Dispatchers.Default
CPU 집중적인 작업을 메인스레드 밖에서 할때
- 리스트를 정렬한다
- JSON parsing
- DiffUtils
Room, Retrofit, Volley 등 라이브러리들은 내부적으로 알아서 main-safety 를 제공하고 있으므로 개발자가 직접 이를 신경써 줄 필요는 없다.
line by line 으로 어떤 Dispatcher 위에서 코루틴이 수행되는지 확인해본다.
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun fetchDocs() {
val result = get("something") // Dispatchers.Main
show(result) // Dispatchers.Main
}
fun show(result : Any) {
println("show something please...")
}
suspend fun get(url: String) // Dispatchers.Main
= withContext(Dispatchers.IO) {
// Dispatchers.IO
// do something IO intensive
} // Dispatchers.Main
코루틴을 쓰면 thread dispatch 를 보다 미세한 수준까지 할 수 있다.
Main 뿐만 아니라 다른 Dispatcher 에서 수행되더라도 안전한 함수인지 보장해주기 위해 withContext 를 사용하는게 좋다.
fetchDocs 는 Main thread 에서 실행되지만, 안전하게 get 을 호출해서 백그라운드에서 요청을 수행한다.
코루틴은 suspend 와 resume 을 지원하기 때문에, 메인 스레드에서 실행되는 코루틴은 withContext 블럭 수행이 끝난 후 그 결과와 함께 resume 가능하다.
잘 짜여진 suspend function 은 항상 main-safe하다 (메인 스레드에서 호출되어도 괜찮다) - Dispatcher 잘 넣어주자.
withContext 의 성능?
콜백 혹은 rx 만큼 빠르다.
database 라이브러리 내부적으로 어떤 요청에 대해서 withContext(Dispatchers.IO) 를 하고 있더라도, outer withContext 로 한번 감싸주면 요청이 10번 가더라도 문맥 전환은 한번이면 된다. -? 여러번의 IO 콜이 있더라도, 하나의 동일한 코루틴 컨택스트 안에서 이루어진다 - ? 오버헤드가 최소화 될 수 있다.
Default 디스패처와 IO 디스패처 사이를 오가는 것 또한 스레드 변경을 피하도록 최적화 되어 있다.
import kotlinx.coroutines.*
// Simulated database access function
suspend fun fetchFromDatabase(query: String): List<String> {
delay(100) // Simulate database access delay
return listOf("Data for $query")
}
// Function to fetch multiple data sets
suspend fun fetchDataSets(): List<List<String>> {
return withContext(Dispatchers.IO) { // Switch to IO dispatcher once
val dataSet1 = fetchFromDatabase("SELECT * FROM users")
val dataSet2 = fetchFromDatabase("SELECT * FROM orders")
val dataSet3 = fetchFromDatabase("SELECT * FROM products")
listOf(dataSet1, dataSetSet2, dataSet3) // Collect and return all data
}
}
// Main function to run the coroutine
fun main() = runBlocking {
val data = fetchDataSets()
data.forEach { dataSet ->
println(dataSet)
}
}
by GPT-4
'Android' 카테고리의 다른 글
3. Kotlin Coroutine - Concurrency issues (0) | 2024.05.15 |
---|---|
2. Kotlin Coroutine - Structured Concurrency, viewModelScope, coroutineScope (0) | 2024.05.15 |
[Compose] Compose Phases (0) | 2024.04.29 |
멜론 UI 에 대한 나의 생각 (0) | 2022.01.26 |
Javascript code 안에 들어가기 (0) | 2021.03.21 |