티스토리 뷰

반응형

[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로만 변환하면 지나치게 많은 중간 함수들이 생길 수 있으므로 상태 기계를 적당히 사용해서 코루틴이 제어를 다른 함수에 넘겨야 하는 시점에서만 컨티뉴에이션이 생기도록 만들 수 있다

 

빌더 직접 만드는건 생략

반응형

'study > kotlin in action' 카테고리의 다른 글

3. 함수 정의와 호출  (0) 2022.01.09
2. 코틀린 기초  (0) 2022.01.01
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
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
글 보관함