일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- rxjava disposable
- 스택 큐 차이
- 안드로이드 라이선스 종류
- 객체
- rxjava hot observable
- 큐 자바 코드
- 서비스 쓰레드 차이
- 2022 플러터 설치
- android ar 개발
- 스택 자바 코드
- Rxjava Observable
- rxjava cold observable
- 클래스
- 안드로이드 레트로핏 사용법
- 멤버변수
- android retrofit login
- 안드로이드 os 구조
- 안드로이드 유닛테스트란
- 서비스 vs 쓰레드
- 안드로이드 유닛 테스트 예시
- ANR이란
- 안드로이드 라이선스
- 안드로이드 레트로핏 crud
- ar vr 차이
- 자바 다형성
- 2022 플러터 안드로이드 스튜디오
- 플러터 설치 2022
- 안드로이드 유닛 테스트
- jvm 작동 원리
- jvm이란
- Today
- Total
나만을 위한 블로그
[Kotlin] 코루틴의 취소와 suspendCancellableCoroutine 본문
suspendCancellableCoroutine(이하 scc)은 코루틴 라이브러리에 속한 API 중 하나로, 취소 가능한 비동기 연산을 가능하게 하고 어떤 코루틴이 취소되면 연산도 취소되게 하는 기능을 지원한다.
물론 코루틴 자체적으로도 cancel()을 지원하고 있다. 그러나 코루틴의 cancel()을 잘못 사용하게 되면 cancel() 호출 후에도 코루틴이 작동을 멈추지 않고 작동할 수 있다. 이것은 코틀린 공식문서에서도 설명하는 내용이다.
https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative
(중략)...kotlinx.coroutines의 모든 suspend function은 취소 가능하다. 코루틴의 취소가 확인되고 취소되면 CancellationException을 발생시킨다. 그러나 코루틴이 계산 작업 중이고 취소를 확인하지 않으면 취소할 수 없다
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // computation loop, just wastes CPU
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
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!
// job: I'm sleeping 3 ...
// job: I'm sleeping 4 ...
// main: Now I can quit.
이에 대응하는 방법은 while 조건의 "i < 5"를 없애고 대신 isActive 키워드를 넣어서, 해당 코루틴이 살아있는지를 따져서 false라면 곧바로 코루틴이 종료되게 하는 것이다. isActive는 CoroutineScope 객체를 통해 코루틴 안에서 사용할 수 있는 확장 프로퍼티다.
그러나 이 방법 외에도 다른 방법이 있다. 그게 이 포스팅의 제목인 SCC다.
공식문서에서 설명하는 SCC는 아래와 같다.
suspendCoroutine과 같이 코루틴을 일시 중단하지만 블록에 CancellableException을 제공한다. 코루틴의 작업이 일시 중단된 동안 취소하거나 완료되면 이 함수는 CancellableException을 발생시킨다. 이 함수의 일반적인 용도는 싱글 샷 콜백 API의 결과를 기다리는 동안 코루틴을 일시 중단하고 호출자에게 결과를 반환하는 것이다. 멀티샷 콜백 API에 대해선 callbackFlow를 참고하라
코루틴의 cancel()은 코루틴이 suspend 상태일 때 코루틴의 작업을 중지시키고 CancellableException을 발생시킨다. 그러나 위에서 말한 대로 비동기 API 호출이나 시간이 오래 걸리는 CPU 작업의 경우 코루틴을 취소해도 해당 작업은 계속 진행될 수 있다.
suspendCancellableCoroutine의 역할은 이 작업을 취소하면서 추가적으로 세세한 작업을 진행할 수 있게 도와준다. 네트워크 호출이나 대량의 데이터를 처리할 경우 불필요한 작업을 중단하고 리소스를 정리하는 작업이 필요할 수 있다. 그 때 유용하게 사용할 수 있다.,
아래는 간단한 suspendCancellableCoroutine의 예시다.
import kotlinx.coroutines.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
fun main() = runBlocking {
val job = launch {
runCatching {
println("코루틴 시작됨")
val result = longRunningTask()
println("result : $result")
}.onFailure {
println("코루틴 취소됨 : $it")
}
}
delay(1000L)
job.cancel()
println("메인 쓰레드")
}
suspend fun longRunningTask(): Int = suspendCancellableCoroutine { continuation ->
// 코루틴이 취소될 경우 invokeOnCancellation으로 정리 작업 실행
continuation.invokeOnCancellation {
println("코루틴이 중간에 취소됨. 정리 작업 시작")
}
// 코루틴을 사용하면서 블로킹 작업이 필요하면 별도 쓰레드에서 실행되게 하는 게 좋다
Thread {
runCatching {
Thread.sleep(3000L)
println("3초 경과")
// 비동기 작업이 끝나면 resume()으로 코루틴 재개 or resumeWithException()으로 예외 발생시킴
continuation.resume(10)
}.onFailure {
continuation.resumeWithException(it)
}
}.start()
}
// 코루틴 시작됨
// 코루틴이 중간에 취소됨. 정리 작업 시작
// 메인 쓰레드
// 코루틴 취소됨 : kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@73035e27
// 3초 경과
runCatching이 생소하다면 try-catch로 바꿔서 작성해도 된다. 둘이 같은 역할을 하지만 runCatching을 쓰는 편이 좀 더 코드가 읽기 좋아서 개인적으로 사용한 것 뿐이다.
longRunningTask()의 블로킹 작업은 별도의 쓰레드에서 실행되고 메인 쓰레드를 중지시키지 않는다. 3초 후에 비동기 작업이 끝나면 resume()을 써서 코루틴을 재개한다. 그리고 비동기 처리 중 에러가 발생해서 onFailure가 호출되면 resumeWithException() 함수를 통해 해당 예외를 밖으로 던진다. 마지막으로 코루틴이 취소된다면 invokeOnCancellation()을 써서 정리 작업을 수행한다. 이 경우 Thread {}의 로직이 실행되지 않는 건 아니다. 정리 작업 수행 후 Thread {}가 작동한다.
참고한 사이트)
https://devroach.tistory.com/162
https://tourspace.tistory.com/442
'개인 공부 > Kotlin' 카테고리의 다른 글
[Kotlin] 함수형(SAM) 인터페이스란? (0) | 2023.09.19 |
---|---|
[Kotlin] 에러 처리 Best Practices (0) | 2023.09.08 |
[Kotlin] use 확장 함수 알아보기 (0) | 2023.07.27 |
[Kotlin] enum 사용 시 values() 대신 entries를 써라? (0) | 2023.07.19 |
[Kotlin] Sealed Class와 Sealed Interface란? (0) | 2023.06.06 |