티스토리 뷰
반응형
[Kotlin in Action - http://www.yes24.com/Product/Goods/55148593]
E.1 코루틴이란?
코루틴은 컴퓨터 프로그램 구성 요소 중 하나로 비선점형 멀티태스킹(non-preemptive multitasking)을 수행하는 일반화한 서브루틴(subroutine)이다. 코루틴은 실행을 일시 중단(suspend)하고 재개(resume)할 수 있는 여러 진입 지점(entry point)을 허용한다. |
- 서브루틴
- 여러 명령어를 모아 이름을 부여해서 반복 호출할 수 있게 정의한 프로그램 구성 요소 == 함수
- 객체지향언어에서는 메소드도 서브루틴이라 할 수 있다.
- 어떤 서브루틴에 진입하는 방법은 오직 한 가지(해당 함수를 호출하면 서브루틴의 맨 처음부터 실행이 시작된다)뿐이며
그 때마다 활성 레코드(activation record)라는 것이 스택에 할당되면서 서브루틴 내부의 로컬 변수 등이 초기화된다 - 서브루틴 안에서 여러 번 return을 활용할 수 있기 때문에 서브루틴이 실행을 중단하고 제어를 호출한 쪽에게 돌려주는 지점은 여럿 있을 수 있다.
- 서브루틴에서 반환되고 나면 활성 레코드가 스택에서 사라지기 때문에 실행 중이던 모든 상태를 잃어버린다.
- 그래서 서브루틴을 여러 번 반복 실행해도(전역 변수나 다른 부수 효과가 있지 않는 한) 항상 같은 결과를 반복해서 얻게 된다.
- 멀티태스킹
- 여러 작업을 동시에 수행하는 것처럼 보이거나 실제로 동시에 수행하는 것
- 비선점형
- 멀티태스킹의 각 작업을 수행하는 참여자들의 실행을 운영체제가 강제로 일시 중단시키고 다른 참여자를 실행하게 만들 수 없다
- 각 참여자들이 서로 자발적으로 협력해야만 비선점형 멀티태스킹이 제대로 작동할 수 있음
- 코루틴 : 서로 협력해서 실행을 주고받으면서 작동하는 여러 서브루틴
- 코루틴 장점
- 일반적인 프로그램 로직을 기술하듯 코드를 작성하고 상대편 코루틴에 데이터를 넘겨야 하는 부분에서만 yield를 사용하면 됨
E.2 코틀린의 코루틴 지원: 일반적인 코루틴
- 코틀린은 코루틴을 구현할 수 있는 기본 도구를 언어가 제공한다(kotlin.coroutine 패키지)
- kotlinx.coroutines 패키지 밑에 코틀린이 지원하는 기본 기능을 활용해 만든 다양한 형태의 코루틴들이 있다
E.2.1 여러 가지 코루틴
- 코틀린에서는 코루틴 빌더에 원하는 동작을 람다로 넘겨서 코루틴을 만들어 실행하는 방식으로 코루틴을 활용한다
- kotlinx.coroutines.core 모듈 밑에 코루틴 빌더(coroutine builder)가 있음
- kotlin.coroutines.CoroutineScope.launch
- launch는 코루틴을 잡(job)으로 반환하며 만들어진 코루틴은 기본적으로 즉시 실행된다
- launch가 반환한 Job의 cancel()를 호출해 코루틴 실행을 중단시킬 수 있다
- launch가 작동하려면 CoroutineScope 객체가 블록의 this로 지정되어야 하는데
다른 suspend 함수 내부라면 해당 함수가 사용 중인 CoroutineScope가 있지만 그렇지 않은 경우에는 GlobalScope를 사용하면 된다 - 유의할 점
- 메인 함수와 GlobalScope.launch가 만들어낸 코루틴이 서로 다른 스레드에서 실행됨
- GlobalScope는 메인 스레드가 실행 중인 동안만 코루틴의 동작을 보장해준다
- 이를 방지하려면 비동기적으로 launch를 실행하거나 launch가 모두 다 실행될 때까지 기다려야 한다
- 코루틴의 실행이 끝날 때까지 현재 스레드를 블록시키는 함수 runBlocking()이 있다
- runBlocking은 CoroutineScope의 확장 함수가 아닌 일반 함수이기 때문에 별도의 코루틴 스코프 객체 없이 사용 가능하다
- 특징
- launch는 즉시 반환된다
- runBlocking은 내부 코루틴이 모두 끝난 다음에 반환된다
- delay()를 사용한 코루틴은 그 시간이 지날 때까지 다른 코루틴에게 실행을 양보한다
- kotlin.coroutines.CoroutineScope.async
- async는 사실상 launch와 같은 일을 한다
- 유일한 차이는 launch가 Job을 반환하는 반면 async는 Deffered를 반환한다
- Deffered는 Job을 상속한 클래스이기 때문에 launch 대신 async를 사용해도 항상 아무 문제가 없다
- Job은 아무 타입 파라미터가 없는데
Deffered는 타입 파라미터가 있는 제네릭 타입이다- Deffered의 타입 파라미터는 바로 Deffered 코루틴이 계산을 하고 돌려주는 타입이다
- async는 코드 블록을 비동기로 실행할 수 있고
async가 반환하는 Deffered의 await을 사용해서 코루틴이 결과 값을 내놓을 때까지 기다렸다가 결과값을 얻어낼 수 있다.- 제공하는 코루틴 컨텍스트에 따라 여러 스레드를 사용하거나 한 스레드 안에서 제어만 왔다 갔다 할 수도 있다
- 실행하려는 작업이 시간이 얼마 걸리지 않거나 I/O에 의한 대기 시간이 크고, CPU 코어 수가 작아 동시에 실행할 수 있는 스레드 개수가 한정된 경우에는 특히 코루틴과 일반 스레드를 사용한 비동기 처리 사이에 차이가 커진다
E.2.2 코루틴 컨텍스트와 디스패치
- launch, async 등은 모두 CoroutineScope의 확장함수이다
- CoroutineScope에는 CoroutineContext 타입 하나만 들어있는데
CoroutineScope는 CoroutineContext를 launch 등의 확장 함수 내부에서 사용하기 위한 매개체 역할만을 담당한다 - CoroutineContext는 코루틴이 실행 중인 여러 작업(Job 타입)과 디스패처를 저장하는 일종의 맵이라 할 수 있다
- 코틀린 런타임은 이 CoroutineContext를 사용해서 다음에 실행할 작업을 선정하고 어떻게 스레드에 배정할지에 대한 방법을 결정한다
E.2.3 코루틴 빌더와 일시 중단 함수
- kotlin-coroutines-core 모듈이 제공하는 코루틴 빌더
- launch
- async
- runBlocking
- produce
- 정해진 채널로 데이터를 스트림으로 보내는 코루틴을 만든다
- ReceiveChannel<>를 반환한다
- 채널로부터 메시지를 전달받아 사용할 수 있다
- actor
- 정해진 채널로 메시지를 받아 처리하는 액터를 코루틴으로 만든다
- 이 함수가 반환하는 SendChannel<> 채널의 send() 메서드르르 통해 액터에게 메시지를 보낼 수 있다
- 일시 중단(suspending) 함수
- delay
- yield
- withContext : 다른 컨텍스트로 코루틴을 전환한다
- withTimeout : 코루틴이 정해진 시간 안에 실행되지 않으면 예외를 발생시키게 한다
- withTimeoutOrNull : 코루틴이 정해진 시간 안에 실행되지 않으면 null을 결과로 돌려준다
- awaitAll : 모든 작업의 성공을 기다린다. 작업 중 어느 하나가 예외로 실패하면 awaitAll도 그 예외로 실패한다
- joinAll 모든 작업이 끝날 때까지 현재 작업을 일시 중단시킨다
E.3 suspend 키워드와 코틀린의 일시 중단 함수 컴파일 방법
- 코루틴이 아닌 일반 함수 속에서 delay()나 yield를 쓰면 예외가 발생한다
- "Suspend function 'yield' sould be called only from a coroutine or another suspend function"
- 일시 중단 함수를 코루틴이나 일시 중단 함수가 아닌 함수에서 호출하는 것은 컴파일러 수준에서 금지된다
- 코틀린은 코루틴 지원을 위해 suspend라는 키워드를 제공한다
- 함수 정의 앞에 suspend를 넣으면 일시 중단 함수를 만들 수 있다
- suspend동작
- 일시 중단 함수 안에서 yield를 할 경우 아래의 동작이 필요
- 코루틴에 진입할 때와 코루틴에서 나갈 때 코루틴이 실행 중이던 상태를 저장하고 복구하는 등의 작업을 할 수 있어야 한다
- 현재 실행 중이던 위치를 저장하고 다시 코루틴이 재개될 때 해당 위치부터 실행을 재개할 수 있어야 한다
- 다음에 어떤 코루틴을 실행할지 결정한다
- 코루틴 컨텍스트에 있는 디스패처에 의해 수행
- 컴파일러는 위의 동작에서 위의 두 작업을 할 수 있는 코드를 생성해내야 하는데 코틀린은 CPS(Continuation Passing Style) 변환과 상태 기계(state machine)을 활용해 코드를 생성해낸다
- CPS 변환은 프로그램의 실행중 특정 시점 이후에 진행해야 하는 내용을 별도의 함수로 뽑고(이런 함수를 Continuation이라 부른다) 그 함수에게 현재 시점까지 실행한 결과를 넘겨서 처리하게 만드는 소스코드 변환 기술이다
- CPS를 사용하는 경우 프로그램이 다음에 해야 할 일이 항상 컨티뉴에이션이라는 함수 형태로 전달되므로, 나중에 할 일을 명확히 알 수 있고, 그 컨티뉴에이션에 넘겨야 할 값이 무엇인지도 명확하게 알 수 있기 때문에 프로그램이 실행 중이던 특정 시점의 맥락을 잘 저장했다가 필요할 때 다시 재개할 수 있다
- CPS를 사용하면 코루틴을 만들기 위해 필수적인 일시 중단 함수를 만드는 문제가 쉽게 해결될 수 있다
- 다만 모든 코드를 전부 CPS로만 변환하면 지나치게 많은 중간 함수들이 생길 수 있으므로 상태 기계를 적당히 사용해서 코루틴이 제어를 다른 함수에 넘겨야 하는 시점에서만 컨티뉴에이션이 생기도록 만들 수 있다
- 일시 중단 함수 안에서 yield를 할 경우 아래의 동작이 필요
빌더 직접 만드는건 생략
반응형
'study > kotlin in action' 카테고리의 다른 글
3. 함수 정의와 호출 (0) | 2022.01.09 |
---|---|
2. 코틀린 기초 (0) | 2022.01.01 |
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- k8s
- kotlin
- Spring
- back merge
- 자바
- java
- ImagePullBackOff
- ddd
- springboot
- 도메인주도설계
- gradle
- linuxkit
- JavaScript
- gasmask
- IntelliJ
- clean code
- 코틀린
- 스프링
- docker for mac
- docker
- 쿠버네티스
- 자바스크립트
- Kubernetes
- 도커
- 클린코드
- docker pull limit
- 스프링부트
- QuickTimePlayer
- kotlin In Action
- cacheable
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함