Kotlin/코틀린 코루틴의 정석

[Kotlin 코루틴] 일시 중단 함수

꾸준함. 2024. 11. 22. 22:50

일시 중단 함수와 코루틴

 

1. 일시 중단 함수란?

  • 주로 코루틴의 비동기 작업과 관련된 복잡한 코드들을 구조화하고 재사용할 수 있는 코드의 집합으로 만드는 데 사용
  • `suspend fun` 키워드로 선언되는 함수로 함수 내에 일시 중단 지점을 포함할 수 있는 특별한 기능을 수행함
    • 일시 중단 지점"은 코루틴(coroutine) 내에서 실행이 일시적으로 중단될 수 있는 지점을 의미
    • ex) delay 함수의 경우 일시 중단 지점을 포함하므로 일반 함수는 delay 함수가 포함시킬 수 없고 오직 suspend fun 함수만 delay 함수를 포함시킬 수 있음

 

 

2. 일시 중단 함수는 코루틴이 아니다

  • 일시 중단 함수는 코루틴 내부에서 실행되는 코드의 집합일 뿐, 코루틴이 아님
  • 일시 중단 함수는 기존의 함수와 똑같은 재사용이 가능한 코드 블록
  • 만약 일시 중단 함수를 코루틴처럼 사용하고 싶을 경우 일시 중단 함수를 코루틴 빌더로 감싸야함

 

3. 일시 중단 함수를 별도의 코루틴 상에서 실행하기

  • 앞서 언급했다시피 일시 중단 함수를 새로운 코루틴에서 실행하고 싶을 경우 일시 중단 함수를 코루틴 빌더 함수로 감싸면 됨

 

 

부연 설명

  • launch 함수가 호출돼 생성된 코루틴들은 실행되자마자 delayAndPrintHelloWorld 함수의 호출로 1초간 쓰레드 사용 권한을 양보함
  • 자유로워진 쓰레드는 다른 코루틴인 runBlocking 코루틴에 의해 사용될 수 있으므로 곧바로 마지막 줄의 getElapsedTime이 실행됨
  • 따라서 코드의 실행 결과를 보면 지난 시간이 약 0초에 가까운 것을 확인할 수 있으며 이후 1초 정도 지나고 나서 재개된 코루틴들에 의해 `Hello World` 문자열이 두 번 출력됨

 

일시 중단 함수의 사용

  • 일시 중단 함수는 내부에 일시 중단 가능 지점을 포함할 수 있기 때문에 일시 중단 할 수 있는 곳에서만 호출 가능
    • 코틀린에서 일시 중단이 가능한 지점은 다음과 같이 두 가지
      • 코루틴 내부
      • 일시 중단 함수

 

1. 코루틴 내부에서 일시 중단 함수 호출하기

  • 일시 중단 함수는 코루틴의 일시 중단이 가능한 작업을 재사용이 가능한 블록으로 구조화할 수 있도록 만들어진 함수
  • 코루틴은 언제든지 일시 중단 함수를 호출할 수 있음

 

 

2. 일시 중단 함수에서 다른 일시 중단 함수 호출하기

  • 일시 중단 함수는 또 다른 일시 중단 함수에서 호출될 수 있음

 

 

부연 설명

  • searchByKeyword 일시 중단 함수는 searchFromDB와 searchFromServer를 호출하며, 결과로 받은 값들을 합쳐 반환
  • 위에서 거론된 함수들은 모두 일시 중단 함수들이며 searchFromDB와 searchFromServer 내부의 delay(1000L)로 인해 1초간 일시 중단돼 결괏값을 반환하는 데 1초 정도 걸림

 

3. 일시 중단 함수에서 코루틴 실행하기

  • launch나 async 같은 코루틴 빌더 함수는 CoroutineScope의 확장 함수로 선언되어 있음
  • 따라서 일시 중단 함수에서 launch나 async 같은 코루틴 빌더 함수를 호출하기 위해서는 일시 중단 함수 내부에서 CoroutineScope 객체에 접근할 수 있어야 함
  • coroutineScope 일시 중단 함수를 사용하면 일시 중단 함수 내부에 새로운 CoroutineScope 객체를 생성할 수 있음
  • coroutineScope는 구조화를 깨지 않는 CoroutineScope 객체를 생성하며 생성된 CoroutineScope 객체는 coroutineScope의 block 람다식에서 수신 객체(this)로 접근 가능

 

 

부연 설명

  • async 코루틴 빌더를 사용해 searchFromDB를 실행하는 코루틴인 dbResultsDeferred와 searchFromServer를 실행하는 코루틴인 serverResultsDeferred를 생성하고, 결과가 반환되는 부분에서 await를 호출해 searchFromDB와 searchFromServer 작업이 함께 실행될 수 있도록 처리
  • 핵심은 searchByKeywordAsync 일시 중단 함수가 호출됐을 때 코루틴이 다음과 같이 구조화된다는 것
    • runBlocking 코루틴에서 searchByKeyword 일시 중단 함수를 호출하면 내부에서 coroutineScope 함수를 통해 새로운 Job 객체를 가진 CoroutineScope 객체가 생성됨
    • 그 자식으로 데이터베이스와 서버로부터 데이터를 가져오는 코루틴이 각각 생성됨
    • 따라서 코드를 실행해 보면 searchFromDB 작업과 searchFromServer 작업이 서로 다른 코루틴에서 실행돼 1초 정도 만에 실행 완료되는 것을 확인 가능

 

 

4. supervisorScope 사용해 일시 중단 함수에서 코루틴 실행하기

  • 3번 코드에 한 가지 문제가 존재함
    • 데이터베이스에서 데이터를 조회하는 코루틴이 오류를 발생시키면 부모 코루틴으로 오류를 전파해 서버에서 데이터를 조회하는 코루틴까지 취소될 수 있음
    • 심지어 일시 중단 함수를 호출한 코루틴에까지 예외가 전파돼 호출부의 코루틴까지 모두 취소됨
    • supervisorScope 일시 중단 함수를 통해 위 문제를 해결할 수 있음

 

 

부연 설명

  • dbResultsDeferred 코루틴에서 예외가 발생해 해당 코루틴이 취소되더라도 부모로 supervisorScope를 통해 생성되는 SupervisorJob 객체를 가지므로 예외가 부모 코루틴으로 전파되지 않는 것을 확인 가능

 

참고

코틀린 코루틴의 정석 (조세영 저)

반응형