Home 코루틴 Job
Post
Cancel

코루틴 Job

최종 수정 날짜 : 2022-05-02 13:40:24 +0900

Coroutine Job

코루틴에서 Job은 백그라운드 작업의 단위를 의미하며 작업이 완료되거나 취소됐을 때 끝나는 생명주기(life-cycle)를 가지고 있다.
Job을 인스턴스화하는 기본적인 방법은 아래와 같다.

  • Coroutine job: launch 코루틴 빌더를 이용해 인스턴스 생성
  • CompletableJob: Job()의 생성자를 이용한 인스턴스 생성
    • Job()의 코드를 살펴보면 아래와 같다.
      1
      
      public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)
      

job은 기본적으로 결괏값을 반환하지 않지만, 결괏값을 반환하려면 async 코루틴 빌더와 같이 Deferred 를 사용해 결괏값을 받을 수도 있다.


Job은 부모(Parent)/자식(Child) 관계가 있다.

1
2
3
4
5
6
7
8
// 부모 (Parent)
val parent = CoroutineScope(Dispatchers.Default).launch {
    [...]
    // 자식 (Child)
    val child = launch {
        [...]
    }
}

부모가 취소되거나 예외가 발생하면 하위 자식들도 동시에 종료가 되어 실행되지 않는다.

  • 정상적으로 실행 시
    정상실행 코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      import kotlinx.coroutines.*
    
      fun main() = runBlocking {
          CoroutineScope(Dispatchers.Default).launch {
              val job2 = launch {
                  println("job2 start")
                  delay(2000)
                  println("Coroutine 2")
              }
              job2.join()
              println("Coroutine 1")
          }.join()
    
          println("Main")
      }
    
    결과
    1
    2
    3
    4
    
      job2 start
      Coroutine 2
      Coroutine 1
      Main
    
  • 부모에서 예외 발생 시
    예외발생 코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      import kotlinx.coroutines.*
    
      fun main() = runBlocking {
          CoroutineScope(Dispatchers.Default).launch {
              val job2 = launch {
                  println("job2 start")
                  delay(2000)
                  println("Coroutine 2")
              }
              throw Exception()
              job2.join()
              println("Coroutine 1")
          }.join()
    
          println("Main")
      }
    
    결과

    image

  • 부모에서 취소 발생 시
    취소발생 코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      import kotlinx.coroutines.*
      import kotlin.coroutines.cancellation.CancellationException
    
      fun main() = runBlocking {
          CoroutineScope(Dispatchers.Default).launch {
              try {
                  val job2 = launch {
                      println("job2 start")
                      delay(2000)
                      println("Coroutine 2")
                  }
                  cancel()
                  job2.join()
                  println("Coroutine 1")
              } catch (e: CancellationException) {
                  println("예외 : ${e.stackTraceToString()}")
              }
          }.join()
    
          println("Main")
      }
    
    결과

    image

자식이 취소가 될 경우에는 해당 Job만 취소되지만 CancellationException을 제외한 나머지 모든 예외는 부모까지 모두 종료가 된다.

  • 자식에서 취소를 발생시킨 경우
    코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
      import kotlinx.coroutines.*
      import kotlin.coroutines.cancellation.CancellationException
    
      fun main() = runBlocking {
          CoroutineScope(Dispatchers.Default).launch {
              val job2 = launch {
                  println("job2 start")
                  // cancel() 또는 throw CancellationException() 시에도 취소가 됨
      //            throw CancellationException()
      //            cancel()
                  delay(2000)
                  println("Coroutine 2")
              }
              job2.cancel()
              println("Coroutine 1")
          }.join()
    
          println("Main")
      }
    
    결과
    1
    2
    3
    4
    
      // job2만 취소가 됨
      job2 start
      Coroutine 1
      Main
    
  • 자식에서 CancellationException이 아닌 다른 예외가 발생한 경우
    코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      import kotlinx.coroutines.*
    
      fun main() = runBlocking {
          CoroutineScope(Dispatchers.Default).launch {
              val job2 = launch {
                  println("job2 start")
                  throw Exception()
                  delay(2000)
                  println("Coroutine 2")
              }
              job2.join()
              println("Coroutine 1")
          }.join()
    
          println("Main")
      }
    
    결과

    image

즉, 자식에서 CancellationException예외를 제외한 다른 예외가 발생할 경우 취소가 부모로 전파되고, 부모가 취소되면서 하위 모든 자식 코루틴이 종료된다.

자식에서 예외가 발생해 부모의 Job까지 취소되는 것을 원하지 않는 경우 SupervisorJob을 이용해 예외가 발생한 Job만 취소할 수 있다.

  • 단, 부모에서 SupervisorJob을 사용하더라도 하위 자식들의 job들은 취소가 된다.

Job States

Job에도 활성(Active), 완료(Complete), 취소(Cancel) 상태가 존재한다. 전체적인 상태 정보는 아래와 같다.

image

image

보통, 일반적으로 사용할 경우 생성과 동시에 활성(Active)상태이지만 코루틴 빌더의 Start 파라미터에 CoroutineStart.LAZY 옵션을 줄 경우 join()또는 start() 메소드를 실행해야만 Job이 실행된다.

코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import kotlinx.coroutines.*

fun main() = runBlocking {
    CoroutineScope(Dispatchers.Default).launch {
        val job2 = launch(start = CoroutineStart.LAZY) {
            println("job2 start")
            println("job state isActive: $isActive [job2 in]")
            delay(2000)
            println("Coroutine 2")
        }
        println("job state isActive: ${job2.isActive}")
        job2.join()

        println("Coroutine 1")
    }.join()

    println("Main")
}
결과
1
2
3
4
5
6
job state isActive: false
job2 start
job state isActive: true [job2 in]
Coroutine 2
Coroutine 1
Main
This post is licensed under CC BY 4.0 by the author.

[프로그래머스] 프린터

Coroutine

Comments powered by Disqus.