BasicsAn explicit jobCoroutines are light-weightCoroutine 실행Coroutine context and dispatchersCoroutine DispatcherCoroutineContextJob in the contextContext element 결합하기Coroutine ScopeThread-local dataDebuggingExceptions HandlingException Propagation
Basics
coroutine은 정지가능한 연산의 인스턴스이다. 코루틴은 개념적으로는 스레드와 비슷한데, 코드블럭을 갖고있고 그 코드블럭을 다른 코드들과 동시적으로 실행한다는 점에서 그렇다. 그러나 코루틴은 특정 스레드에 묶여있지 않고 한 스레드에서 실행을 중지시키고 다른 스레드에서 실행을 재개할 수 있다.
launch
: coroutine builder. 다른 코드와 함께 새로운 코루틴을 concurrent 하게 시작함(독립적으로 동작하게 됨)
delay
: suspending function. coroutine을 명시한 시간만큼 정지시켜줌. coroutine을 suspend 하는 것은 실행되고 있던 스레드를 block 하지 않고 다른 코루틴이 그 스레드를 사용할 수 있음
runBlocking
: 또한 coroutine builder 로서 일반적인 함수인 fun main() 과 coroutine 사이를 연결해주는 역할을 함.runBlocking
이 없으면launch
call을 할 수 없음.launch
는CoroutineScope
안에서만 사용 가능함- runBlocking은 이를 실행시키는 스레드(위에서는 main 스레드)가 runBlocking을 호출하는 동안(runBlocking 내부의 모든 coroutine이 실행을 완료하기 까지) block 된다는 것을 의미
- application의 top-level에서는 많이 쓰는 것을 볼 수 있지만 실제 코드 안에서는 많이 사용안함. 스레드는 비싼 자원이기에
An explicit job
Coroutines are light-weight
- 아래 코드가 위의 코드보다 더 많은 메모리를 잡아 먹게 됨.
Coroutine 실행
- CoroutineScope를 생성
- CoroutineScope() 정적함수 호출 → 실행의 흐름을 방해하지 않음. 따로 block 되지 않음
- runBlocking() 을 통해 coroutineScope 생성 → block 이 됨
- CoroutineScope 안에서
launch
나async
를 통해 coroutine 실행
Coroutine context and dispatchers
코루틴은 항상 CoroutineContext 타입의 값으로 대표되는 어떠한 context 안에서 실행된다.
코루틴 context는 다양한 요소의 집합으로 이루어져 있는데, 메인 요소는 코루틴의
Job
이고, 그리고 그것의 dispatcher
임Coroutine Dispatcher
어떤 스레드 혹은 스레드들이 해당 coroutine 의 실행을 담당할지를 결정하는 것이 Coroutine Dispatcher가 하는 역할임
Coroutine Dispatcher는 코루틴의 실행을 특정 스레드에 할당할수도, 스레드 풀에 할당할 수도, 한정짓지 않고 실행되게 할 수도 있음
모든 Coroutine builder(
launch
, async
) 가 optional 한 CoroutineContext 값을 받는데 이 값이 dispatcher를 명시하는데에 사용됨- launch가 파라미터 없이 실행되면 실행된 CoroutineScope의 context를 물려받음
Default
: 공유된 백그라운드의 스레드 풀을 이용
newSingleThreadContext
: 코루틴이 실행될 스레드를 생성. dedicated 된 스레드는 비싼 자원이기에 실제 어플리케이션에서는 해당 스레드는 필요없어지면 꼭 할당을 해제해주어야 함
Unconfined
: 처음에는 caller thread에서 코루틴을 시작. 근데 중지되고 나서 다시 실행될 때는 호출된 suspending 에 의해 실행되는 thread 가 정해진다. 이 dispatcher 타입은 CPU time 을 많이 소모하지 않거나 특정 스레드에 한정된 공유 데이터를 업데이트 해야하는 경우가 아닐때 적합함
기본적으로 Dispatcher는 바깥의 CoroutineScope에서 사용하는 것을 그대로 물려받음
CoroutineContext
Job in the context
코루틴의 Job은 context의 일부분으로써, 위의 표현식을 통해 조회가 가능함
Context element 결합하기
- + 연산자로 coroutine context에 여러 요소들을 정의할 수 있음
- dispatcher
- courinteName
- coroutineExceptionHandler
Coroutine Scope
CoroutineScope는 그 안에서 실행된 모든 coroutine을 한번에 cancel 할 수 있게 감싸주는 역할을 함(memory leak 이 발생하지 않도록)
CoroutineScope 인스턴스는 CoroutineScope() 나 MainScope() 정적 함수들에 의해 생성될 수 있음
- CoroutineScope 는 일반적인 목적의 scope 이고 MainScope()는 UI 어플리케이션에 대한 scope를 만듦
Thread-local data
thread local 데이터를 코루틴 간에 전달해서 계속적으로 공유하게 하는 것은 때때로 편리하다
ThreadLocal의 asContextElement 확장 함수를 이용하면 coroutine이 thread 를 변경했을때마다 그 값을 그대로 불러와 준다.
그러나 한가지 주요 한계점은 thread local의 값이 바뀌면 새로운 값은 전파가 되지 않음. 다른 coroutine으로 갈때 변경된 값이 아닌 처음에 셋팅한 값으로 불러와짐. thread-local의 값을 코루틴 안에서 변경하려면
withContext
함수를 사용하라.Debugging
- Intellij에서 Debugging 가능
- Run the following code with
-Dkotlinx.coroutines.debug
JVM option:
Exceptions Handling
Exception Propagation
- launch : exception 을 그냥 uncaught exception 으로 처리함
- async, produce : user가 final exception 을 consume 할지에 따라 달라짐(await 나 receive를 호출하면)