Composing Suspending Functions
이번 페이지는 suspending functions의 구성을 알아본다.
Sequential by default
기본에 기반한 연속성.
1> 위 함수는 1초정도의 지연이 있는 어떤 유용한 기능을 하는 함수를 가상적으로 만들어낸 것이다.
2> 만약에 두 함수를 순차적으로 invoke하게 되었을때 코루틴은 1번째 함수를 invoke하고 그 작업이 완료 된 후 2번쨰 함수를 invoke하여 총 2초 이상의 시간이 소요될 것이다.
3> 이는 coroutine 특성상 기본적인 옵션이 sequential하기 때문이고 아래 코드로 시간을 측정하여 그 결과를 확인할 수 있다.
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
기억할 키워드
measureTimeMillis
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
Concurrent using async
비동기를 사용한 동시성.
1> 만약에 위의 2개 함수가 dependencies가 없어 독립적으로 수행할 수 있다면 sequential하게 실행할 이유가 없고 독립적으로 각각 실행하여 수행시간을 단축시킬 수 있을 것이다. 이때 사용하는 키워드는 async이다.
2> 개념적으로보면, [async]는 [launch]와 비슷하다. async는 다른 코루틴들과 동시적으로 동작하는 경량쓰레드인 separate된 코루틴으로 시작한다. 이 둘의 차이점은... launch는 Job을 return하고, 어떠한(any) 결과값을 가지지(carry)않는 반면에 async는 Deferred를 리턴한다 ㅡ Deferred는 경량 non-blocking future로서 결과를 나중에 제공하는 promise를 표현(represent)하는 것이다(future, promise, Defer에 대해선 코루틴 포스팅이 끝나면 자세히 다룰 예정입니다, 2020-09-21).
3> deferred 값에서는 체인룰로 .await()를 사용하여 최종 결과(eventual result)를 얻을수 있고, Deferred 또한 [Job]이므로 (앞선 포스팅에서 다룬 것과 마찬가지로) 필요하다면 cancel을 사용할 수 있다.
위 코드를 실행한다면 1000ms 내에서 작업 2개가 끝나는 것을 확인할 수 있다.
코루틴의 동시성을 사용하기 위해서는 명시해야된다는 것에 유의하자.
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
기억할 키워드
measureTimeMillis
async
Deferred(Deferred)
await()
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
Lazily started async
추가적으로(Optionally)... 스타트 파라미터인 CoroutineStart.LAZY를 설정하여 async를 lazy형태로도 만들 수 있다.
이를 사용한다면 await가 결과를 요청했을 떄, 혹은 Job의 시작함수가 invoke되었을 때야 비로소("비로소"는 비문입니당..) aysnc가 동작하도록한다.
1>위 예제는 이전 예제처럼 정의하자마자 곧바로 실행되지 않는다. start() 를 호출했을때야 비로소 코루틴이 동작하고 개별 코루틴은 finish가 되기까지 await한다.
2> 만약 개별 코루틴들이 처음에 start를 호출하지않고서 println문 안에 있는 await를 호출하였다면, await는 코루틴을 start하고 그것이 끝날때까지 wait하기 때문에, 코루틴은 sequential하게 동작할 것이다. 이는 처음에 의도한 laziness에 대한것과는 거리가 멀다. 이때 aysnc는 suspending function을 포함하는 값 계산이 있을 경우의 async(start = Corou....LAZY)에 대한 실제 사용은 standard한 lazy를 대체한다.
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
기억할 키워드
measureTimeMillis
async
Deferred(Deferred)
.await()
async(start = CoroutineStart.LAZY)
.start()
-*-*--*-*--*-*--*-*--*-*--*-*--*-*-
Async-style functions
1>비동기스타일 함수. 위의 예제에서 에이싱크 스타일의 함수(async-style function)을 정의하고 invoke하여 명시적인 GlobalScope에 async coroutine builder를 사용함으로써 비동기적으로 함수를 구성할 수 있었다.
2>이러한 함수를 ...Async라는 접미사를 넣어 이러한 결과(이 함수들은 start를 통해 비동기적인 computation을 가지고 있고, 결과를 얻기위해서 각각은 deferred value의 resulting된 값이 요구된다는 의미가 포함된 접미사)를 나타낼 것이라는 사실을 highlighting 할 수 있다(아래 그림을 참조)
3>여기서, xxxAsync functions는 suspending functions가 아님에 유의하자. 이들은 어디서나 사용할 수 있다. 하지만 이들의 사용은 invoking code에 의해 이루어지는 asynchronous execution(비동기적인 시작)을 암시한다(여기서 이 의미는 concurrent이다).
아래 코드는 코루틴 바깥에서의 이들의 사용에 대한 예를 보여준다
4>그런데 위 예는 async function에 대하여 설명하기 위해서 잠깐 나타낸 프로그래밍 스타일인데.. 다른 언어에 있어 자주 쓰이는 방식이기 때문이다. 하지만 우리 코틀린에서의 코루틴 스타일에서는 위의 예제는 강력하게 비권장 사항이다. 이 이유는 아래에 제시하겠다.
5> 만약에 val one = somethingUsefulOneAysnc() 라인과 one.await() 사이에 어떠한 코드상의 로직에러가 있고 프로그램이 exception을 throw하고, 프로그램 중단에 의한 operation이 수행되었다고 가정해보자(Consider what happens if between the val one = somethingUsefulOneAsync() line and one.await() expression there is some logic error in the code and the program throws an exception and the operation that was being performed by the program aborts)
일반적으로 전역 에러 헨들러(global error-handler)는 이러한 예외(exception)에 대해 catch를 할 수 있고, log과 에러에 대해 개발자에게 레포팅(report)한다. 그런데 (프로그램이 중단되어야함에도 불구하고) 그와 동시에 그 프로그램은 다른 동작을 계속 할 가능성도 있다. 이 코드에서 확인할 수 있듯이, 예외로 인해 그 작동이 중단되었음에도 불구하고, somethingUsefulOneAysnc가 여전히 백그라운드에서 동작하는 문제가 있다.
이러한 문제는.... 아래 섹션(section)에서 확인할 수 있듯이 구조적인 동시성에서는 이와 같은 발생을 하지 않게 만듬으로써 해결할 수 있다.
Structured concurrency with async
async를 사용한 동시성 예제를 생각해보자. 그 예제는 동시적으로 동작하는 doSomethingUsefulOne과 doSomethingUsefulTwo함수와 그 결과를 합으로 리턴하는 요소가 있다. async 코루틴 빌더는 CoroutineScope에 extension으로 정의되어 있기 때문에, 그 범위(scope)내에서 그것(async coroutine builder)을 넣어야하며.. 그것이 바로 coroutineScope 함수가 제공하는 것이다.
위 예제처럼, concurrentSum 함수의 내부코드가 잘못되어 예외를 던지는 상황에서, 그것들의 범위내에서 launched 된
모든 코루틴은 cancel 된다. 위의 결과를 보듯이, 두 operation은 여전히 동시적으로 실행이 되고 있다.
Cancellation은 항상 코루틴 하이라키를 통해서 전파된다.
'Programming Theory > 코루틴(Coruotine)' 카테고리의 다른 글
Coroutines guide, kotlinx-coroutines-core / flow (0) | 2022.02.01 |
---|---|
Coroutines guide + Flow 정리(2022) (0) | 2022.01.22 |
kotlin Coroutine 정리(4, Coroutine Context and Dispatchers) (0) | 2020.09.22 |
Kotlin Coroutine 정리(2, Cancellation and Timeouts) (0) | 2020.09.17 |
Kotlin Coroutine 정리(1, Basics) (1) | 2020.09.16 |