Coroutines 취소 1편 - Coroutine 실행 취소하기, Coroutines 취소는 협력적이다, Coroutine의 Computation 코드를 취소 가능하게 만들기

2023. 2. 15. 23:33·공식 문서 번역/Coroutines 공식 문서
반응형

이 섹션은 코루틴 Cancellation과 Timeout에 대해 다룹니다.

 

Coroutine 실행 취소하기

긴 시간동안 실행되는 어플리케이션에서 백그라운드에서 실행되는 Coroutine에 대한 세밀한 제어가 필요할 수 있다. 예를 들어, 유저가 Coroutine을 실행시킨 페이지를 닫아 결과가 더 이상 필요하지 않아 작업이 취소되어도 되는 경우이다. launch 함수는 실행중인 코루틴을 취소하는 데 사용할 수 있는 Job 객체를 반환한다.

 

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다.
    println("main: I'm tired of waiting!")
    job.cancel() // Job을 cancel한다.
    job.join() // Job의 실행이 완료될 때까지 기다린다. 
    println("main: Now I can quit.")    
}

📌 전체 코드는 이곳에서 확인할 수 있습니다.

 

 

이는 다음 결과를 출력한다.

job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.

main 함수가 job.cancel을 호출하면, Job이 취소되었기 때문에 다른 코루틴의 출력을 확인할 수 없다. Job의 확장 함수로 cancel과 join 호출을 결합한 cancelAndJoin 또한 있다. 

 


이 글은 Coroutines 공식 문서를 번역한 글입니다.

 

원문 : Cancellation and timeouts - Cancelling coroutine execution

원문 최종 수정 :  2022년 6월 27일

 


Coroutines 취소는 협력적이다

Coroutine의 취소는 협력적이다. Coroutine 코드는 취소 가능하도록 협력해야 한다. kotlinx.coroutines 패키지의 모든 일시 중단 함수들은 취소 가능하다. 그들은 Coroutine이 취소되었는지 확인하고 취소되었을 경우 CancellationException을 발생시킨다. 만약 코루틴이 계산 작업 중이고 취소를 확인하지 않는다면, 다음의 예시처럼 취소될 수 없다.

 

import kotlinx.coroutines.*

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (i < 5) { // 계산 루프, CPU를 낭비한다
            // 1초에 두 번 메세지를 출력한다.
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Job을 취소하고 실행이 완료될 때까지 기다린다.
    println("main: Now I can quit.")    
}

전체 코드는 이곳에서 확인할 수 있습니다.

 

*그림1. 위 코드를 실행한 결과

 

코드를 실행하여 취소가 실행된 이후에도 다섯번의 반복 후에 Job이 완료될 때까지 "I'm sleeping"이 계속해서 프린트 되는 것을 보자. 

 

같은 문제가 CancellationException을 catch 하고 다시 throw 하지 않는 경우에도 생긴다.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch(Dispatchers.Default) {
        repeat(5) { i ->
            try {
                // 1초에 두 번 메세지를 출력한다.
                println("job: I'm sleeping $i ...")
                delay(500)
            } catch (e: Exception) {
                // 예외를 로깅한다.
                println(e)
            }
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다.
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Job을 취소하고 완료될 때까지 기다린다.
    println("main: Now I can quit.")    
}

전체 코드는 이곳에서 확인할 수 있습니다.

 

예외를 catch 하는 것은 안티 패턴이지만, 이 문제는 CancellationException을 다시 throw 하지 않는 runCatching 함수를 사용하는 경우와 같이 미묘한 경우에도 일어날 수 있다.

 

 


📖 아래 내용은 독자의 이해를 위해 번역자가 추가한 글입니다.

*그림1은 공식 가이드에 없는 그림 입니다. 이해를 위해 추가하였습니다.


이 글은 Coroutines 공식 문서를 번역한 글입니다.

 

원문 : Cancellation and timeouts - Cancellation is cooperative

원문 최종 수정 :  2022년 6월 27일

 


Coroutine의 Computation 코드를 취소 가능하게 만들기

computation code를 취소 가능하게 만드는 두가지 접근 방식이 있다. 첫 째는 주기적으로 일시 중단 함수를 실행시켜서 취소되었는지 확인하도록 하는 것이다. 이 목적을 위해 좋은 방식인 yield 함수가 있다. 다른 하나는 명시적으로 취소 상태를 확인하도록 하는 것이다. 후자의 접근 방식을 시도해보도록 하자.

 

이전 예시*1의 while (i < 5)를 while (isActive)로 변경한 후 다시 실행 시켜보도록 하자.

 

import kotlinx.coroutines.*

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (isActive) { // 취소 가능한 computation loop
            // 1초에 두 번 메세지를 출력한다.
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Job을 취소하고 실행이 완료될 때까지 기다린다.
    println("main: Now I can quit.")    
}

📌 전체 코드는 이곳에서 확인할 수 있습니다.

 

이제 이 loop가 취소되는 것을 볼 수 있다*2. isActive는 CoroutineScope 객체를 통해 Coroutine 내부에서 사용할 수 있는 확장 프로퍼티이다.

 

 


📖 아래 내용은 독자의 이해를 위해 번역자가 추가한 글입니다.

*1. 이전 예시 코드는 다음과 같다. 이 코드의 while 문 내부에서는 Cancellation을 확인할 수 있는 부분이 없다.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (i < 5) { // 계산 루프, CPU를 낭비한다
            // 1초에 두 번 메세지를 출력한다.
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Job을 취소하고 실행이 완료될 때까지 기다린다.
    println("main: Now I can quit.")    
}

 

*2. 바뀐 코드를 실행한 결과는 다음과 같다.

 

* yield를 사용해도 같은 결과가 나온다. 별도 글로 분리 예정

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        while (i < 5) { // 계산 루프, CPU를 낭비한다
            yield()
            // 1초에 두 번 메세지를 출력한다.
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L) // 약간의 시간 동안 delay 한다
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Job을 취소하고 실행이 완료될 때까지 기다린다.
    println("main: Now I can quit.")
}

이 글은 Coroutines 공식 문서를 번역한 글입니다.

 

원문 : Cancellation and timeouts - Making computation code cancellable

원문 최종 수정 :  2022년 6월 27일

 

 

 

반응형

'공식 문서 번역 > Coroutines 공식 문서' 카테고리의 다른 글

Coroutines 실행 시간 제한하기 - Timeout, 비동기 Timeout과 리소스  (0) 2023.02.17
Coroutines 취소 2편 - finally를 사용해 리소스 닫기, 실행 취소가 불가능한 블록 실행하기  (0) 2023.02.16
Coroutines 기초 알아보기 2편 - Coroutines Scope Builder 사용하기, Coroutine Job 명시적으로 사용하기, Coroutines는 light-weight 이다  (2) 2023.02.14
Coroutines 기초 알아보기 1편 - 첫 Coroutines 작성하기, 함수 추출해 Coroutines 코드 리펙토링하기, Coroutines Scope Builder 사용하기  (1) 2023.02.13
Kotlin Coroutines 공식 문서 한국어 번역본  (1) 2023.02.12


'공식 문서 번역/Coroutines 공식 문서' 카테고리의 다른 글
  • Coroutines 실행 시간 제한하기 - Timeout, 비동기 Timeout과 리소스
  • Coroutines 취소 2편 - finally를 사용해 리소스 닫기, 실행 취소가 불가능한 블록 실행하기
  • Coroutines 기초 알아보기 2편 - Coroutines Scope Builder 사용하기, Coroutine Job 명시적으로 사용하기, Coroutines는 light-weight 이다
  • Coroutines 기초 알아보기 1편 - 첫 Coroutines 작성하기, 함수 추출해 Coroutines 코드 리펙토링하기, Coroutines Scope Builder 사용하기
심플코드
심플코드
프로그래밍을 어렵지 않게 풀어서 설명하는 기술 블로그
    반응형
  • 심플코드
    심플코드
    심플코드
  • 전체
    오늘
    어제
    • 분류 전체보기 (96)
      • 안드로이드를 위한 Coroutines (2)
      • Unit Testing (19)
      • GitHub Actions (0)
      • 공식 문서 번역 (35)
        • Coroutines 공식 문서 (35)
      • 알고리즘 (7)
        • Kotlin 자료구조 (0)
        • 알고리즘 (7)
        • Kotlin으로 구현하는 자료구조 (0)
      • 코딩 테스트 (0)
      • Deep Learning (0)
      • Machine Learning Math (17)
        • Linear Algebra (17)
      • ML (0)
      • Docker (15)
      • Kubernetes (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 코틀린 코루틴의 정석 책 출간 소식
  • 인기 글

  • 태그

    unit test
    pytorch
    컨테이너
    Coroutines Flow
    mockito
    Coroutines
    코루틴 Flow
    Docker
    코루틴
    Machine Learning
    Coroutines Channel
    코루틴 채널
    TensorFlow
    Kotlin
    coroutine
    numpy
    unit testing
    도커
    Coroutines Context
    junit
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
심플코드
Coroutines 취소 1편 - Coroutine 실행 취소하기, Coroutines 취소는 협력적이다, Coroutine의 Computation 코드를 취소 가능하게 만들기
상단으로

티스토리툴바