
비동기적으로 실행되는 코드를 간소화하기 위해 안드로이드에서 사용할 수 있는 동시 실행 설계 패턴
Kotlin 뿐만 아니라 Python, C#, JS 등 여러 언어에서 지원하는 개념임
코루틴을 사용하면 상대적으로 간단하고 가독성 높게 동시성 관리가 가능함
"코루틴이란 실행의 지연과 재개를 허용함으로써, 비선점적 멀티태스킹을 위한 서브루틴을 일반화한 컴퓨터 프로그램 구성요소이다" - 위키피디아 정의
- 비선점형 멀티태스킹: 하나의 Task가 Scheduler로부터 CPU 사용권을 할당받았을 때, Scheduler가 강제로 CPU 사용권을 뺏는 것이 불가능함
- 즉, 스레드는 실행되다가 OS에 의해 제어권이 뺏길 수 있으나(선점형), Coroutine은 중단 지점을 만나지 않는 한 제어권을 양도하지 않고 이어서 계속 실행함(비선점형)

위의 그림처럼 하나의 스레드 안에 여러 개의 코루틴이 존재할 수 있음
(스레드는 작업이 실행되는 곳이며, 코루틴이 하나의 작업인 것)


-> 이때 B가 일반적인 함수였다면 로컬 변수를 초기화하면서 처음부터 실행을 다시 시작하겠지만, 코루틴이면 이전에 yield로 실행을 양보했던 지점부터 실행을 계속하게 되는 것
따라서 코루틴을 사용하는 경우, 일반적인 프로그램 로직을 기술하듯 코드를 작성하고
상대편 코루틴에 데이터를 넘겨야 하는 부분에서만 yield를 사용하면 됨!
코루틴은 OS가 아닌 스레드 내에서 다른 코루틴에게 제어권을 넘겨주기 때문에,
스레드 내의 모든 코루틴은 OS에서 관리하는 다른 스레드에 제어권을 양도하지 않고 해당 스레드의 Time Frame을 계속 이용할 수 있음
즉, 코루틴은 OS가 아닌 사용자에 의해 시분할을 달성하게 됨
따라서 스레드를 통해 동시성 프로그래밍을 할 경우
CPU가 매번 스레드를 점유했다가 놓아주는 과정을 반복해야 하기 때문에 많은 비용이 들지만, 코루틴을 사용하면 더 적은 비용이 든다
또한 스레드는 CPU 사용과 시스템 오버헤드라는 관점으로 볼 때 유한한 리소스라는 점이 문제임
내부적으로 스레드 생성, 스케줄링, 파기를 위해 많은 작업이 진행됨
현대적인 CPU들은 수많은 스레드를 실행할 순 있지만, 특정 시점에 병렬적으로 실행될 수 있는 숫자는 CPU 코어 수로 제한됨 (보통 안드로이드 기기들의 CPU는 4 코어)
CPU 코어 숫자보다 많은 수의 스레드가 필요하면, 시스템은 스레드 스케줄링을 수행해 사용할 수 있는 코어들 사이에서 이 스레드들의 실행을 공유할 수 있는 정책을 결정함
따라서 실행하려는 작업이 시간이 얼마 걸리지 않거나, I/O에 의한 대기 시간이 크고, CPU 코어 수가 작아 동시에 실행할 수 있는 스레드 개수가 한정된 경우
코루틴으로 비동기 처리를 하면 좋음
Dispatch의 뜻은 '보내다'로, 디스패처는 스레드에 코루틴을 보내는 역할을 함
즉, 코루틴을 만든 다음 해당 코루틴을 디스패처에 전송하면 디스패처는 자신이 관리하는 스레드풀 내의 스레드 부하 상황에 맞춰 코루틴을 배분함

위의 그림에서 디스패처는 쉬고 있는 Thead2에게 Coroutine3을 배분하게 됨
안드로이드에는 이미 디스패처가 생성되어 있기 때문에, 별도로 생성하거나 정의할 필요가 없음
coroutineScope.launch(Dispatcher.IO) {
performSlowTask()
}
코루틴은 코루틴 스코프 안에서만 동작함
특히 일시중단 함수(await, join, delay, suspend)는 코루틴 스코프 안에서만 호출이 가능함
코루틴 스코프를 사용하기 위해서는, 미리 정의된 코루틴 스코프를 사용하거나 직접 생성하는 방법이 있음
fun main() {
GlobalScope.launch {
// run coroutine
}
}
val scope = CoroutineScope(Dispatchers.Main)
val job = scope.launch {
// run coroutine
}
코루틴을 실행할 때 사용하는 여러가지 함수
val job = CoroutineScope(Dispatchers.IO).launch() {
delay(3000)
println("launch 완료")
}
CoroutineScope(Dispatchers.Default).launch() {
val networkingDeferred = async(Dispatchers.IO) {
delay(3000)
"some networking.."
}
Log.d(TAG, "${networkingDeferred.await()}")
}
GlobalScope.launch(IO) {
val result = fetch(httpUrl)
withContext(Main) {
resultTextView.text = result.toString()
}
}
runBlocking {
delay(5000)
println("step 1")
}
println("step 2")
도서 '핵심만 골라 배우는 젯팩 컴포즈'
https://hodie.tistory.com/72
https://kotlinworld.com/141
https://origogi.github.io/coroutine/%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%8A%A4%EC%BD%94%ED%94%84/
https://jutole.tistory.com/15
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ko
https://taehyungk.github.io/posts/android-kotiln-coroutine-async-await/