CS/Android

Coroutine (2)

졔졔311 2024. 12. 24. 09:35
728x90
반응형

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개를 병렬로 실행, 하나가 실패해도 나머지가 실행되길 원할 경우

 

 

출처 : Coroutines basics | Kotlin Documentation

728x90
반응형

'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