오늘은 코루틴의 suspend와 blocking에 관해 정리를 해보고자 한다.
안드로이드에서 설명한 코루틴을 한 번 같이 살펴보자
코루틴은 concurrency design pattern이다.
기본 스레드를 차단하며 ANR까지 이끄는 장기 실행 작업을 관리하는데에 용이하다.
실행되는 스레드를 찬단하지 않고 suspend
키워드로 단일 스레드에서 많은 코루틴을 실행할 수 있다.
구조화된 동시성 (CoroutineScope)을 사용한다
코루틴 계층 구조(Coroutine hierarchy) 를 통해 취소가 가능하다
Jetpack 라이브러리에 코루틴 지원을 제공하는 확장 기능들이 있다
처음에 코루틴 문서을 읽다보면 대체 코루틴에서 특징적으로 꼽는 suspend와 blocking이 뭔데? 그 차이는 뭔데? 궁금할 수 있다.
용어에서 오는 백그라운드가 설립이 안됐기 때문인데, 하나씩 정리하다보면 이해가 되는 지점이 온다
"Blocking"은 말그대로 특정 작업이 완료될 때까지 스레드의 실행을 중지하는 행위다. 스레드가 차단되면서 본질적으로 일부 리소스나 작업이 완료되기를 기다리고 있으며 해당 시간 동안 다른 작업을 수행할 수 없다.
리소스 활용 측면에서 효율성이 떨어질 수 있으며 안드로이드 앱에서 해당 행위는 매우 좋지 않다. 사용자의 편의성이 좋지 않을뿐더러 ActivityManagerService에서 ANR을 발생시킬 수도 있다.
(만약 터치 한 번 하는데 5초마다 멈췄다가 다음 액션을 취한다고 생각해보자. 매우 잘못됐다)
실제로 코드를 짜서 본다면 아래와 같다.
networkRequest()
는 메인 스레드를 블로킹할 것이다. 이가 완료하는데 몇 초 또는 몇 분이 걸릴 수 있고 메인 스레드가 업데이트되지 않아 사용자 액션에 반응이 없어 멈춰있는 것처럼 보일 것이다.
suspend 영어의 뜻은 뭘까? 유예하다. 연기하다. 매달다라는 뜻을 갖고 있다. 뭔가 이렇게 영어로만 봤을 땐 감이 잡히지 않는다. 심지어 suspension은 정지라는 뜻을 가진 명사가 되기도 한다.
코루틴에서 suspend
는 정지할 수 있는 함수이다 즉, 코루틴이 suspend 함수를 발견하면 기본 스레드를 차단하지 않고 실행을 정지시킬 수 있다. 그 동안 다른 코루틴이 계속 실행될 수 있다.
한 마디로 메인 스레드를 유예시키고, 지연시키고, 코루틴을 실행시키는 것이 suspend의 함수다.
suspend는 스레드가 block됐다는 의미가 아니다. 아래 그림을 확인해보자
Blocking 코드와 똑같아보이지만 suspend
를 사용하기 때문에 networkRequest()
가 호출됐을 때 메인 스레드를 blocking하는 대신 코루틴을 suspend(일시 중단)한다
그리고 메인 스레드가 UI의 onDraw() 메서드를 처리할 수 있도록 한다.
그 후, 코루틴의 result가 준비되면 다시 전달되면서 resume
이 된다.
즉, 네트워크 요청을 실행하는 것은 여전히 다른 스레드를 사용한다는 것이다. 그러나 콜백 버전보다 blocking보다 코드의 간단함과 효율성을 챙길 수 있다.
사실 blocking과 suspend의 차이는 별게 없다. 아예 작업을 차단을 하냐 마냐의 차이다. 여기서 주목할 점은 코루틴의 suspend가 어떤 배경으로 나오게 됐고 어떤 의미를 함축하냐가 중요하다.
suspend 키워드를 가진 함수가 코루틴에서 어떻게 동작하는지 확인해보면 더욱 좋다
그러기 때문에 이 글은 백그라운드 정리용으로만 보고 아래 코드랩 영상을 한 번 보는 것을 추천한다!
https://www.youtube.com/watch?v=ne6CD1ZhAI0
대략 짧게나마 정리하면, suspend 함수가 호출이 된다면 해당 함수를 복사해뒀다가 main thread의 액션이 일어나고 resume을 하는 식이다.
(이 때의 함수 호출 스택은 general 하지 않다. state machine으로 관리가 되는데 자세한 내용들은 아래 첨부한 영상 링크를 참고하자!)
resume은 Continuation
인터페이스를 통해 호출이 된다.
예를 들어 어느 하나의 함수가 있고 이 함수를 코틀린 컴파일러가 어떻게 치환을 하는걸까? 아래와 같이 변환이 일어난다.
extra parameter가 생기면서 해당 completion의 함수가 호출되는 형식이다. 특이점은 기존 suspend의 return 타입도 사라진다.
하지만 suspend 함수 내에서 다른 suspend 함수가 호출이 될 것이고 모두 sequential하게 처리가 되는데 이럴 때는 label을 붙여 관리한다.
그리고 suspend 함수의 StateMachine 객체가 generate되어 우리가 익히 하는 코루틴 함수에서의 동작들이 가능해진다.
실제로 suspend 함수를 작성해 바이트 코드로 바꿔보면 아래와 같다
그래서 실제로 suspend 함수의 디컴파일된 코드를 보면 함수 파라미터로 Continuation 객체가 들어가 있는 것을 확인할 수 있고 label
과 ContinuationImpl
도 볼 수 있다.
이 Continuation 관련해서도 영상을 한 번 보는 것을 추천한다 :)
https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency
https://june0122.tistory.com/17
https://yveskalume.medium.com/kotlin-coroutines-suspending-vs-blocking-73d0194507ab
https://developer.android.com/kotlin/coroutines
https://m.blog.naver.com/gngh0101/222290810537
https://nuritech.tistory.com/16