-
코루틴(Coroutine) - 기본개념Android(+ Kotlin) 2020. 1. 16. 16:17
[2.18 수정]
코루틴은 Kotlin언어를 개발한 jetbrains에서 만들어졌다.(해당 원론 개념은을 뜻한바는 아니였습니다.)ko.wikipedia.org/wiki/%EC%BD%94%EB%A3%A8%ED%8B%B4
"Kotlin 1.3에서 추가되었으며 다른 언어에서 확립된 개념을 기반으로 합니다"(developer.android.com/kotlin/coroutines?hl=ko)
Kotlin언어에서 사용할 수 있는 coroutine 비동기 솔루션입니다.(Java에서는 사용할 수 없다.)
서브루틴(subroutine)
먼저 서브루틴 개념이 필요하다,
하나의 함수를 예를 들어 파라미터를 받고 시작해서 끝 지점에서 종료되는 방식이 서브루틴이다.
코루틴(Cooutine)
서브루틴가 유사하게 단일지점에서 시작해서 끝 지점에서 종료가 된다.
하지만 중간에 함수 실행을 중단되었다 중단 지점 지점에서 재 진행한다.(임의 지점에서 멈춤, 해당 지점에서 재개)
결국 비동기형 프로그래밍으로 보아도 무방할 것 같다.
- 그럼 코루틴의 장점은 무엇일까?
- 비동기 코드를 작성할 때 asynctask와 같이 별도의 클래스 또는 스레드를 생성할 필요 없이 순서대로 코드를 작성할 수 있다.
- light-weight thread이다. 기존 비동기 코드(asynctask, thread) 보다 소모비용이 작다,
- google에서 AsynckTask가 deprecate 되며 코루틴을 추천하고 있고 있다.
- 현재 코틀린 1.3 버전부터 공식적으로 추가되었으며 mvvm등에 구조에 맞는 scope 제공한다.(대세는 corutine!!)
rxjava 보다 러닝 커브는 낮다고 생각이 든다. 하지만 rx가 많은 기능을 갖고 있기 때문에 앱 기능에 대해 더 체크해보고 비동기 라이브러리를 선택해야 한다. (서버와의 api통신 정도의 어플은 coroutine정도도 충분할 것이다.)
https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html
공식 문서 기반으로 정리해 보았다.
기본 사용방법
GlobalScope.launch { delay(1000) println("World!") } println("Hello, ") Thread.sleep(2000)
sleep을 한 이유는? - 코루틴은 비동기이라 실행 중 메인 프로세스가 종료되면 해당 작업이 함께 종료된다.
runBlocking
해당 runBloking은 기본적으로 CoroutineScope를 내장하고 있다. (GlobalScope.launch 도 해당 스코프를 내장)
runBlocking { delay(2000L) }
메인 스레드 진행을 막고 해당 범위가 종료된 후 다음 프로세스를 진행한다. 위 sleep(2000)을 위 코드로 대처할 수 있다.
Job
코루틴의 동작을 컨트롤할 수 있다. launch를 통해 Job 타입으로 반환 값이 발생된다.
* Job에서 사용할 수 있는 메서드
- start - 동작 상태 반환(동작중 - true)
- join - 동작이 끝날 때까지 대기한다. (메모리에서 종료가 될 때까지 대기한다.)
- cancel - 종료 요청한다.
- cancelAndJoin - 종료 요청과 함께 메모리에서 종료가 될때까지 대기한다.
- cnacelChileren - 부모를 제외한 하뤼 루틴들을 종료한다.
Job관련 상세 설명 사이트 - https://thdev.tech/kotlin/2019/04/08/Init-Coroutines-Job/
더 자세한 내용은 위 링크를 참조하자!
val job = GlobalScope.launch { delay(1000L) println("World!") } println("Hello, ") job.join()
위 sleep, runBlocking을 join으로 대처
리팩토링 함수 추출
launch 내부를 보다 깔끔한 코드로 리팩토링 할 때 아래와 같이 함수를 만들어서 사용이 가능하다.
suspend라는 키워드를 사용하여 언제든 지연 및 재개될 수 있는 함수로 정의할 수 있다.(IDE에서 빨간 밑줄이 표시되며 자동완성을 통해 조정된다.)
fun main() = runBlocking { launch { doWorld() } println("Hello,") } suspend fun doWorld() { delay(1000L) println("World!") }
doWorld() 함수 내부의 delay동안 코루틴이 일시 중단된다. 그동안 다른 작업이 진행되며 다시 print문이 실행된다.
취소(Cancel)
fun main() = runBlocking { var job = launch { repeat(1000) {i -> println("job: I'm sleeping $i ...") delay(500L) } } delay(1300L) println("main: I'm tired of waiting!") job.cancel() job.join() // 위 처럼 종료, 대기로 작업 job.cancelAndJoin() // 간단하게 한줄로 작업 가능하다. println("main: Now I can quit.") }
진행 중 취소가 가능하며 1~1000까지 반복되나 중간에서 cancel을 통해 종료 요청이 들어가 종료가 된다.
join or cancelAndJoin 사용하여 보다 안정적으로 프로세스를 종료하자!
delay(1300L) println("1300 waiting end time : ${Date().time - nowTime}") println("main: I'm tired of waiting!") job.cancel() job.join() // job.cancelAndJoin() println("call by cancel time : ${Date().time - nowTime}") println("main: Now I can quit.")
0.004초 만에 종료가 되었다.(성능에 따라 상이)
join or cancelAndJoin 메서드를 사용하면 0.010초 만에 종료되었다. 0.006초 가량 차이가 났는데, 이 이유는 join에서 메모리 해제까지의 작업에 소요되는 시간이 포함되어 있을 것이라고 추측할 수 있다.
타임아웃(TimeOut)
코루틴이 실행이 중일 때 join을 통해 프로세스를 마냥 기다릴 수 없다 android에서는 ANR 가능성을 배제할 수 없다. 이럴 때를 대비한 기능인 듯하다.
fun main() = runBlocking { withTimeout(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } }
위 코드와 같이 실행하면 TimeoutCancellationException 이 나타난다. 1.3초 동안 진행 후 타임아웃을 걸었기 때문이다.
exception을 throws 혹은 try/catch를 통해 예외처리를 진행하여도 되지만 좋은 방식이 아니다.(개인적 생각)
withTimeoutOrNull을 활용하면 해당 시간이 초과되었을 때 null을 return 한다. 아래 방식이 더 안정적이라 생각이 든다.
fun main() = runBlocking { val result = withTimeoutOrNull(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } println("Done") // 해당 프로세스가 제시간에 완료되었을때 나타남. } println("Result is $result") }
끝.
'Android(+ Kotlin)' 카테고리의 다른 글
코루틴(Coroutine) - 입문2(Asynchronous Flow) (0) 2020.02.06 코루틴(Coroutine) - 입문 (1) 2020.01.22 MVVM 따라하기 Data Binding + LiveData(2) (0) 2020.01.09 [수정] ViewModel의 ViewModelProvider (0) 2019.12.30 MVVM 따라하기 Data Binding, LiveData (1) (0) 2019.12.20