2024.12.17 - [CS/Android] - Coroutine (1)
Coroutine (1)
ㅇ coroutine이란?- 실행을 일시 중단(suspend)/다시 실행(resume) 시킬 수 있는 기술- 비동기 프로그래밍을 위해 사용되기 때문에, 개념적으로는 일종의 경량 스레드(light-weight thread)로 볼 수 있지만, 특
jyejye311.tistory.com
(1)에서는 coroutine이 무엇인지, coroutine을 어떻게 실행할 수 있는지 공부했다.
coroutine은 JVM thread에 비해 메모리 소요가 적어 가볍기 때문에 선호되는 사용 방식이다.
import kotlinx.coroutines.*
fun main() = runBlocking {
repeat(50_000) { // launch a lot of coroutines
launch {
delay(5000L)
print(".")
}
}
}
fun main() {
repeat(50_000) { // launch a lot of threads
Thread {
Thread.sleep(5000L) // Simulate delay
print(".")
}.start()
}
}
이 아래 코드는 thread의 수가 너무 많아지면 JVM의 허용량을 넘어서 문제가 발생할 수 있다.
반면에 위의 코드는 coroutine이 각각 thread를 사용하는 것이 아니므로 리소스 사용량이 적다.
이런 장점이 있기에, coroutine에 대해 잘 아는게 중요하고, 그렇기에
이번에는 더 복잡해진 버전의 coroutine 사용법을 공부할 것이다.
ㅇconcurrent coroutines
// 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
doWorld() suspending 함수 내부에 두 개의 concurrent coroutine을 넣었다.
첫 번째 launch와 두 번째 launch는 동시에 실행되고,
doWorld()가 리턴 후 runBlocking에서 다시 실행되기 때문에,
Hello -> World1 -> World2 -> Done 순서로 출력된다.
ㅇ explicit job - job.join()
fun main() = runBlocking {
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
main에서 시작되는 부모 coroutine 안에 job coroutine을 추가하였다.
여기서 job.join()을 호출함으로써, 자식 coroutine(=job)이 수행될 때까지 아래 println("Done")이 실행되지 않는다.
즉, World!가 출력되어야 Done이 출력될 수 있다.
이런 방식으로 coroutine의 순서를 적절히 조절할 수 있다.
ㅇ explicit job 2 - SupervisorJob
val myScope = CoroutineScope(Dispatchers.IO + Job())
코드를 보던 중, 이런 부분을 발견했다.
내가 배운건 CoroutineScope(Dispatchers.IO) 이런식으로 정의하는게 전부였는데, 여기는 "+ Job()"이 붙어서 굉장히 의아했다..
CoroutineScope는 coroutine context를 인자로 받는다.
- coroutine context : coroutine의 실행 환경을 정의하는 요소. 여기서는 Dispatchers.ID와 Job()으로 정의함.
- Dispatchers.IO : I/O 작업을 위한 thread pool에서 coroutine 실행
- Job() : 이 scope에 속한 모든 coroutine의 생명 주기 제어하는 부모 Job
- serviceScope.launch{ ... } 와 같이 실행 가능 - 해당 코드 내에서 정의된 fun
val myScope2 = CoroutineScope(Dispatchers.IO + SupervisorJob())
위와 같이 정의된 코드도 존재했다.
이 경우에는, Job()으로 쓰이는게 아니라 SupervisorJob()이라는 문법이 새로 등장하였다.
- SupervisorJob()
- coroutine hierarchy에서 독립적인 오류 처리가 가능하도록 만든 Job.
- Job()의 경우, 상위 coroutine이 취소되면 하위 coroutine도 모두 취소
하나의 하위 coroutine이 실패하면 그 실패가 부모에 전달, 부모가 취소되면서 모든 형제 coroutine이 취소.(연쇄적) - SupervisorJob()에서 상위 coroutine은 하위 coroutine의 실행 상태를 모니터링하고 관리만.
- 따라서 독립적인 병렬 작업이 필요할 때 사용
- ex) 네트워크 요청 5개를 병렬로 실행, 하나가 실패해도 나머지가 실행되길 원할 경우
'CS > Android' 카테고리의 다른 글
SDK와 PDK (1) | 2025.01.06 |
---|---|
Build Variant (1) | 2024.12.26 |
Coroutine (1) (0) | 2024.12.17 |
공통 객체 이용하기 - CompositionLocal (0) | 2024.12.17 |
Navigation (0) | 2024.12.16 |