IT/Kotlin

[Kotlin/Coroutine] 3. launch와 async의 Job과 Deferred

2023. 4. 7. 17:43
목차
  1. launch 함수의 Job
  2. async 함수의 Deferred
  3. Job의 상태
  4. Job 상태변수
  5. Job의 상태는 한 방향으로만 이동한다
  6. launch와 async의 Lazy한 실행 방법 (지연 실행)
  7. Coroutine 시작 지점에 대한 속성
  8. Join()을 활용한 동시성 제어
  9. Job을 활용해 여러 개의 Coroutine을 한번에 관리하는 방법
  10. 부모 Job과 자식 Job
  11. Job의 취소
반응형

launch 함수의 Job

  • launch함수로 시작한 Coroutine은 Job 타입의 객체를 Return한다
  • Job은 coroutine 자체를 의미하기에, launch로 실행되는 Coroutine을 취소할 수도 있고, 기다릴 수도 있다.
    → 즉 LifeCycle 에 관심이 있다.

async 함수의 Deferred

  • 여기서 Deferred는 Job을 상속한 클래스이기 때문에, launch를 사용한 곳에 async를 사용해도 항상 아무 문제가 없다.
  • async는 return할 결과값이 있는데, Deferred<T>타입으로 객체를 return한다.
  • Deferred는 Future처럼 미래의 결과값을 담아놓을 수 있는 객체이며 결국 미래의 결과값을 의미하므로 완료되길 기다리다가 Return되는 값에 관심이 있다.
  • Deferred는 공식문서에서 Non-Blocking Cancellable Future라고 되어있는데, 내부를 보면 Job을 상속받기에, Job의 역할도 할 수 있는 것을 알 수 있다. 따라서 이는 Job을 확장해서 가지는 성격이라고 볼 수 있다. 그리고 Future은 Return값을 가지기 때문에 Future같은 역할을 한다.
    • 즉, 결과값을 가지는 Job이라는 의미에서 Non-Blocking Future라고 표현된 것이라 볼 수 있다.

Job의 상태

  • Job의 상태는 총 6가지로 정의 된다

  • New → Active → Completing → Completed
  • Canceling → Cancelled.

Job 상태변수

  1. isActive : Job이 실행중인지 여부 표시
    (coroutineScope 에서의 isActive는 사실 coroutineContext[Job]?.isActive == true의 Shortcut이다.)
  2. isCompleted : Job의 실행이 완료되었거나, cancel이 완료되었는지 표시
  3. isCancelled : Job cancel이 요청되었는지 여부를 표시
상태 / 상태변수 isActive isCompleted isCancelled
생성됨 New false false false
실행 중 Active true false false
실행 완료 Completing true false false
취소 중 Cancelling false false true
취소 완료 Cancelled (최종 상태) false true true
실행 완료 Completed (최종 상태) false true false

Job의 상태는 한 방향으로만 이동한다

  • Job은 특정 상태에 도달하면, 이전 상태로 되돌아가지 않는다.
runBlocking {
val job = CoroutineScope.launch {
delay(2000)
}
job.join()
//Restart
job.start()
job.join()
}
  • 위 코드에서 처음 호출한 job.join()이 완료되면, 완료됨상태에 도달했으므로, start()를 호출해도, 아무런 변화가 없다.

launch와 async의 Lazy한 실행 방법 (지연 실행)

  • Job과 Deferred은 생성과 동시에 실행하기 때문에, 필요한 위치에 바로 생성해서 실행해야 하는 문제로, Coroutine 실행의 유연성이 떨어질 수 밖에 없다. 이를 해결하기 위해 필요할 때 수행하도록 할 수 있다.
  • Job이나 Deferred를 미리 만들어두고, 특정 시점에 Coroutine을 실행하도록 하면 된다.
  • Job을 생성하는launch나 async 메소드에 인자로, start=CoroutineStart.LAZY를 넣으면 된다
    • Lazy하게 생성된 Job은 2가지 방법으로 실행 가능하다
      1. start() : 일시 중단 없이 실행되는 메소드
        • 생성된 Coroutine 작업(Job)을 일시 중단 없이 실행한다
          → 실행 위치를 Coroutine 내부나 suspend function 내부로 바꾸는 것이 필요치 않다
          → suspend function에서 호출하지 않아도 된다.
      2. join() : Job이 완료될때 까지 Job이 실행되고 있는 Coroutine을 일시중단 한다
        • 실행을 일시중단 할 수 있기 때문에 suspend 함수 내부에서 호출해야 한다
      • 예시
suspend fun main() {
val job = CoroutineScope(Dispatchers.IO).launch(start = CoroutineStart.LAZY) {
println("가나다")
}
job.start()
delay(100L)
}
// delay가 없다면 결과값이 출력되지 않는다
// 문제는 작업에 얼마나 걸릴 지 알수 없기에 delay를 늘리는 것은 잘못된 코딩이라는 것이다.
// 이를 위해 이러한 작업이 끝날 때 까지 일시중단 해주는 join() 메소드를 사용하는 것이다.
suspend fun main() {
val job = CoroutineScope(Dispatchers.IO).launch(start = CoroutineStart.LAZY) {
println("가나다")
}
job.join()
}
// 위와 동일하게 출력된다

Coroutine 시작 지점에 대한 속성

  • 위에서 Lazy이외에 필요한 경우, launch()나 async()에 인자를 지정해, Coroutine에 필요한 속성을 줄 수 있다.
  • launch와 async의 함수의 parameter를 보면 알 수 있듯 start 매개변수를 통해 지정해주면 된다.
    • DEFAULT : 즉시 시작
    • LAZY : Coroutine을 느리게 시작(처음에는 중단된 상태이며, 함수로 시작 필요)
    • ATOMIC : 최적화된 방법으로 시작
    • UNDISPATCHED : 분산 처리 방법으로 시작

Join()을 활용한 동시성 제어

  • Job 내부 함수로, 동시성을 제어할 수 있는 함수이다.
  • 순서를 정해야 하는 경우 join을 통해 순차적으로 실행되도록 할 수 있다. (순서관리)
  • → Coroutine 내부에 여러 launch 블럭이 존재하는 경우, 모두 새로운 Coroutine으로 분기되어 동시 실행하기에, 순서를 정할수 없다
  • 모든 자식 job이 완료될 때까지, 특정 job과 연관된 Coroutine을 정지한다
  • 이러한 구조들이 Structured Concurrency의 기반을 마련한다.
  • → 각 Coroutine의 참조를 직접 관리할 필요 없이, Coroutine이 더이상 오래 실행되지 않게 할수 있다는 뜻이다.
  • 예시
CoroutineScope(Dispatchers.Default).launch {
launch {
for (i in 0..5){
delay(500)
Log.d("코루틴", "$i")
}
}.join()
launch {
for (i in 6..10){
delay(500)
Log.d("코루틴", "$i")
}
}
}

 

  • 예시 2
val job = launch {
repeat(1_000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L)
println("main: I'm tired of waiting!")
job.cancel()
job.join() // job 이 완료될 때까지 기다림
println("main: Now I can quit.")
  1. launch 블럭은 job Type을 Return
  2. job은 실행중인 Coroutine을 취소할 수 있다.
    • Job의 취소는 아래에서 추가적으로 다룬다.
  3. job.cancel() + job.join() = job.cancelAndJoin()
    • cancelAndJoin()은 특정 Job을 취소한다

Job을 활용해 여러 개의 Coroutine을 한번에 관리하는 방법

  • Job은 coroutine 자체를 의미한다.
  • Coroutine에 대한 Job객체를 반환받아, Coroutine을 제어할 수 있다.
  • 그렇다면 한 CoroutineScope내에 여러 개의 Coroutine이 존재하고,
    그 Coroutine을 한 번에 관리하기 위해서는?
    • Job 또한 CoroutineContext의 일종이기 때문에 이를 이용한다.
      Job은 coroutineContext[Job]을 통해 얻어올 수 있다.
      coroutineContext는 coroutineScope에서 바로 접근 가능하다.
    • 하나의 Job 객체를 선언한뒤, 새로 생성되는 CoroutineScope에서 객체를 초기화하면,
      이 CoroutineScope의 자식들까지 모두 영향을 받는 Job으로 활용이 가능하다.
fun main() = runBlocking {
val job = Job()
CoroutineScope(Dispatchers.Default + job).launch {
launch {
println("coroutine1 start")
delay(1000) println("coroutine1 end")
}
launch {
println("coroutine2 start")
delay(1000)
println("coroutine2 end")
}
}
delay(300)
job.cancel()
delay(1000)
println("all done !!!")
}

부모 Job과 자식 Job

  • Coroutine의 LifeCycle를 Job으로 관리할 수 있고 (Scope와 Coroutine의 생명주기 지정가능)
  • 내부에서 다시 builder를 호출하면 자식 Job으로 생성된다.
    • 부모 Job 취소시, 자식 Job도 취소되며, 그 반대는 취소되지 않는다.
    • launch로 생성한 자식 Job은 예외 발생시 부모 Job을 취소시킨다
    • async로 생성한 자식 Job은 예외가 발생해도 부모 Job이 취소되지 않는데, 이는 반환 결과에 예외도 포함시켜버리기 때문이다.
      • 자식이 Exception을 뱉어버리면 전역으로 퍼지게 되어 일을 중단하게 되는데,
        Exception이 발생하게 될 때, 부모에게는 퍼지지않게 하기위해, SupervisorJob()을 사용하게 된다. 그렇다면, 자식들이 취소되어도 다른 자식들은 이어서 일을 진행할 수 있다.

Job의 취소

  • Job은 항상 실행에 성공하여 실행 완료 상태가 되지 않는다. 다양한 변수로 인해 중간에 취소되어야 할 수 있다.
    • 예를 들어, 서버와 클라이언트간의 통신의 경우 클라이언트가 서버에 요청하였으나 응답을 지속적으로 받지 못하는 경우, 일정 시간 이후 Job을 취소하는 과정이 필요하다.
  • 이러한 Job의 취소의 경우에는 이후의 Coroutine의 취소에서 다루도록 한다.
반응형

'IT > Kotlin' 카테고리의 다른 글

[Kotlin/Coroutine] 5. Coroutine의 예외처리와 취소  (0) 2023.05.08
[Kotlin/Coroutine] 4. Coroutine의 구성요소: Coroutine의 생성과 활용  (0) 2023.04.22
[Kotlin/Coroutine] 2. Kotlin에서의 Coroutine: launch와 async  (0) 2023.03.24
[Kotlin/Coroutine] 1. Coroutine이란 무엇인가  (0) 2023.03.16
[Kotlin] Delegate Pattern  (0) 2023.02.07
  1. launch 함수의 Job
  2. async 함수의 Deferred
  3. Job의 상태
  4. Job 상태변수
  5. Job의 상태는 한 방향으로만 이동한다
  6. launch와 async의 Lazy한 실행 방법 (지연 실행)
  7. Coroutine 시작 지점에 대한 속성
  8. Join()을 활용한 동시성 제어
  9. Job을 활용해 여러 개의 Coroutine을 한번에 관리하는 방법
  10. 부모 Job과 자식 Job
  11. Job의 취소
'IT/Kotlin' 카테고리의 다른 글
  • [Kotlin/Coroutine] 5. Coroutine의 예외처리와 취소
  • [Kotlin/Coroutine] 4. Coroutine의 구성요소: Coroutine의 생성과 활용
  • [Kotlin/Coroutine] 2. Kotlin에서의 Coroutine: launch와 async
  • [Kotlin/Coroutine] 1. Coroutine이란 무엇인가
Hodie!
Hodie!
완벽이 아닌 향상을 추구합니다
Hodie!
Vive Hodie
Hodie!
  • 분류 전체보기
    • IT
      • CS
      • Kotlin
      • Android
      • Algorithm
      • Network
      • 정보기기운용기능사
    • ETC
      • 회고록

블로그 메뉴

  • 홈
  • 방명록

인기 글

최근 댓글

최근 글

hELLO · Designed By 정상우.
Hodie!
[Kotlin/Coroutine] 3. launch와 async의 Job과 Deferred
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.