코루틴에 대해 공식 문서와 블로그를 참고한 글입니다.
비동기 프로그래밍: 메인 스레드에서 시간이 오래 걸리는 작업을 하게 되면, Application Not responding이 발생함 즉 메인 스레드가 특정 시간동안 응답하지 않으므로, 오래걸리는 작업을 안드로이드에서는 메인스레드와 분리해서 처리하도록 한다.
기존의 스레드 모델을 경량화한 형태로, JVM 레벨에서 관리되어 운영체제의 스레드보다 훨씬 가볍다.
수십만 개의 스레드를 생성할 수 있으며, I/O 작업 시 효율적으로 대기 상태에 들어가서 시스템 자원을 적게 소모한다.
스레드는 운영체제에 의해 관리되는데, 여러 CPU 코어에서 병렬로 작업 실행이 가능하다.
스레드를 생성하면 해당 스레드의 스택에 메모리를 할당하고 스레드 간 전환을 수행한다. 그렇기 때문에 많은 리소스를 소모하게 된다.
코루틴도 JVM의 스레드처럼 코드를 동시 실행하는 일시 중단 가능한 연산이지만, 내부적으로는 다르게 작동한다.
코루틴은 여러 스레드에 종속되지 않는다!
한 스레드에서 일시 중단되었다가, 다른 스레드에서 다시 시작할 수가 있다. 즉 여러 코루틴이 동일한 스레드 풀을 공유할 수 있다. 코루틴이 일시 중단되어도 해당 스레드는 다른 작업을 실행할 수 있는 것이다. 스레드보다 자원을 과도하게 소모하지 않아도 된다.
코루틴 스코프의 launch라는 함수가 있는데...
현재 스레드를 차단하지 않고 새로운 코루틴을 시작하고, 해당 코루틴에 대한 참조를 job 객체로 반환한다.
생성된 job이 취소되면 해당 코루틴도 취소된다.
즉 스코프 기반으로 실행 환경이 결정된다.
그래서 scope.launch 를 실행하게 되면,
부모 job과 dispatcher를 상속받는다!
suspend 함수는 보통 원격 서비스 호출, 계산과 같은 유용한 작업을 수행한다.
suspenc 함수로 정의를 하게 된다면 , 함수는 순차적으로 호출이 된다.
함수들을 동시에 실행하고 싶다면, 이럴 때 비동기 프로그래밍이 유용한다.
비동기는 다른 모든 코루틴과 동시에 작동하는 별도의 코루틴을 시작한다. launch 함수랑 유사한데, launch는 결과 값을 전달하지 않고 Job 객체를 반환한다.
비동기는 반면에 결과값을 받을 수가 있다.
반환 값이 Defferred인데, 미래에 결과를 받을 수 있는 객체이다. 즉 아직 결과가 없고, 나중에 꺼내는 것이다.
핵심은 await 함수 호출이다.
result.await() 이런식으로 호출하면 결과를 반환해준다!
이렇게 호출되면 스레드 블로킹이 되는 게 아니라, 결과값이 나올 때까지 코루틴만 잠시 멈춘다.
async는 반드시 await를 해야 의미가 있다. 결과값을 사용을 안한다는 것이므로 launch랑 다를 게 없다.
coroutineScope {
val one = async { ... }
val two = async { ... }
}
코루틴스코프 안에서 aysnc를 호출해주게 되면, 스코프가 끝나기 전에 무조건 다 끝나야만 한다.
그래서 자동으로 await되는 것 처럼 보인다.
왜냐, 코루틴 설계 자체가 부모가 자식이 끝나기 전에 절대 끝나지 않는다는 철학이 있어서이다.
내부적으로는
스코프 시작 -> one -> two -> 둘 다 끝날 때까지 기다림 -> 스코프 종료
이렇게 되기때문에 무조건 끝나는 것이다. 즉 스코프 자체가 suspend 함수 역할을 한다.
여기서 주의사항은, 코루틴 스코프의 async 자식 중 하나라도 실패하면 첫 번째 부모와 대기 중인 부모가 모두 취소된다.
코루틴이 언제까지 실행할지 관리하는 것이다.
Scope가 종료되면 그 안에서 실행 중이던 모든 코루틴도 자동으로 멈춘다.
순서
코루틴을 실행하면 코루틴스코프 실행을 제어하는 컨텍스트가 생성된다.
코루틴은 코루틴스코프의 컨텍스트를 그대로 물려받는다. 구성 요소는 다음과 같다.
코루틴 디스페처는 해당 코루틴이 실행에 사용할 스레드를 결정한다.
코루틴 실행을 특정 스레드로 제한하거나, 스레드 풀로 디스패치하거나, 제한 없이 실행하도록 할 수 있다.
launch, async 와 같은 코루틴 생성 함수는 선택적으로 컨텍스트 매개변수를 허용한다.
매개변수 없음: 매개변수가 없다면 코루틴은 실행되는 코루틴스코프의 컨텍스트를 상속받는다. 부모 또는 메인 코루틴의 컨텍스트를 가지는 것이다.
Dispatchers.Unconfined: 제한 되지 않으며 메인 스레드에서 작동한다.
Dispatchers.Default: DefaultDispatcher로 디스패치된다. 범위 내에 다른 디스패처가 명시적으로 지정되지 않는 경우, 기본 디스패처가 사용된다는 뜻이다. 공유 백그라운드 스레드 풀을 사용한다.
newSingleThreadContext : 코루틴 실행을 위한 스레드를 생성한다. 전용 스레드는 많은 자원을 사용한다는 뜻이므로 , 더 이상 필요하지 않을 때 close 함수를 사용해서 해제하거나 최상위 변수에 저장해서 재사용해야 한다.
Dispatcher.IO:
- 읽기 쓰기 작업에 최적화되어 있다.