What is Coroutine?
정의
Co(함께) + Routine(작업 단위) = 작업들을 함께 수행한다
특징
- 비선점형 멀티태스킹
- Task(작업)가 생성되면 OS의 Scheduler가 가지고 있는 대기 queue에 우선순위에 따라 작업이 들어가게 됩니다.
- Scheduler는 대기 queue에 올라가 있는 우선순위에 따라 실행 queue로 작업을 이동시키고 CPU core에 올립니다.
- Scheduler는 정책에 따라 core에 올라간 작업이 충분히 실행되었다고 생각하면 작업이 끝나지 않아도 다시 대기 queue로 작업을 이동시킵니다.
- 이것이 바로 선점형 방식
- 따라서 비선점형이라 하면 작업이 끝나기 전에는 임의적으로 작업을 회수할 수 없는 방식을 이야기 합니다.
- 지연(suspend)과 재개(resume)
- Coroutine을 사용하면 다른 작업(B작업)이 진행이 되면 현재 진행중이던 작업(A작업)을 잠시 멈춤니다.(suspend)
- B작업이 끝나면 중지되었던 부분부터 A작업을 재개합니다.(resume)
- 동시성(Concurrency)
- Thread 같은 경우 실제 다중 코어를 통해 여러 작업을 같은 시간상에 수행합니다. → 병행성
- Coroutine은 하나의 Thread로 작동되지만 작업을 빠르게 전환하면서 한 번에 작동이 되는 것처럼 보이게 합니다. → 동시성
- 다수의 루틴이 정해진 규칙에 따라 실행 시간을 나눠 갖습니다.
- 경량 스레드 (Light-Weight Thread)
- Thread의 경우 독립적 메모리 영역인 stack을 할당받지만, Coroutine은 heap 영역을 공유합니다.
- Thread는 작업이 Interrupt되서 context switching이 되면 switching된 Thread로 stack(메모리)을 바꿔줘야 합니다.
- Coroutine은 하나의 Thread에서 동작하기 때문에 context switching에 따른 메모리 오버헤드가 적습니다.
👩🏻💻 Process : 메모리에 적재되고 CPU 자원을 할당받아 프로그램이 실행되고 있는 상태 (독립적 메모리와 스택을 가짐)
👩🏻💻 Thread : 한 프로세스 내에서 동시에 진행되는 작업 흐름의 단위 (공유 메모리와 독립적 스택을 가짐)
- 안드로이드에서 비동기 작업을 할 때 코루틴을 사용할 것을 권장
- Lifecycle, ViewModel 등 Jetpack 함께 사용했을 때 좋은 어플리케이션을 만들기에 유용합니다.
- 이전엔 AsyncTask를 사용했는데 메모리 누수를 포함한 다양한 문제들로 API30에서 Deprecated 되었습니다.
Coroutine과 Thread 비교
Coroutine | Thread |
협력형으로 동작, 병행형으로 동작하지 않음 | 개수에 따라 완전히 병행적으로 동작 |
Scheduler가 실행시점을 결정하지 않고, 이벤트에 의해 실행, 지연, 재개 시점 결정 | OS shceduler가 실행 시점을 결정(Scheduler가 선점 가능) |
비선점형 멀티태스크로 동시성 제공 | 선점형 멀티태스크로, 멀티 프로세싱과 병행성 제공 |
독립적 스택을 가질 수도 있지만 일반적으로 스택을 재생성하지 않음 | Thread별 독립적 스택을 가짐 |
구동 방식
다음과 같은 Routine1, 2를 구동한다고 가정하겠습니다.
먼저 Routine1의 명령 1을 수행합니다.
어느 정도 수행하면 명령1은 잠시 대기하고(suspend) Routine2의 명령4를 수행합니다.
명령4도 어느 정도 수행을 하면 잠시 대기하고, suspend 상태였던 명령1을 이전에 처리했던 부분 이후부터 재실행(resume)합니다.
Coroutine의 구성요소
Coroutine Scope
💡 코루틴이 실행되는 영역
- GlobalScope
- 애플리케이션 전역에서 사용 가능한 Scope
- 수명 주기 관리 없이 코루틴을 실행합니다.
- 주로 앱 전체에서 긴 수명의 작업을 수행하는 데 사용합니다.
- 하지만 최상위 레벨의 싱글톤이기 때문에 필요 시 생성과 삭제를 반복하기 어렵습니다.
- 구글에서도 사용을 권장하진 않습니다.
- CoroutineScope
- 특정 범위에서 코루틴을 관리하는 데 사용합니다.
- 일반적으로 액티비티, 프래그먼트, ViewModel 같은 수명 주기 관리 구성 요소와 연결됩니다.
- 연결된 수명 주기 종료 시 코루틴을 자동으로 취소해 메모리 누수 방지와 안전한 코루틴 관리가 가능합니다.
Coroutine Context
💡 코루틴이 실행되는 환경 정보 제공
Dispatchers
- 코루틴이 실행될 Thread or Thread pool을 지정합니다.
- 디스패처를 통해 코루틴이 어떤 Thread에서 실행될지 결정됩니다.
Dispatcher 종류
- Dispatcher.Main
- UI(Main Thread) 업데이트와 관련한 작업에 적합합니다.
- Dispatcher.Default
- CPU 연산이 많은 작업에 적합합니다.
- Dispatcher.IO
- File, Network와 같은 I/O 작업에 적합합니다.
- Dispatcher.unconfined
- UI Thread에서의 긴 작업을 피하기 위해 사용되는데, 잘 사용하지 않는다고 합니다.
Job
- 추상적인 개념의 코루틴 흐름을 제어하기 위해 사용하는 Object입니다.
- State가 존재해 flag를 보면서 상태를 체크할 수 있습니다.
- isActive
- isCompleted
- isCancelled
- Differed는 반환 값이 있는 Job을 말합니다.
Coroutine Builder
💡 코루틴을 생성하고 실행하는데 사용되는 함수
- launch
- Job 객체를 반환하여 반환값이 존재하지 않습니다.
- async
- Differed 객체를 반환하여 반환값이 존재합니다.
- withContext
- 디스패처를 변경하고 원래의 디스패처로 복귀하기 위해 사용합니다.
- runBlocking
- 메인 함수 내에서 코루틴을 실행하기 위해 사용됩니다.
- 메인 스레드를 블로킹하고 내부 코루틴이 끝날때까지 대기합니다.
Coroutine의 지연
- delay
- Thread의 sleep과 유사합니다.
- join
- launch로 실행한 Coroutine을 job의 실행이 끝날 때까지 대기합니다.
- await
- async로 실행한 Coroutine을 Differed의 실행이 끝날 때까지 대기합니다.
Example
예를 들어 다음과 같은 코드가 있다고 가정해봅시다.
class MainActivity : AppCompatActivity() {
val TAG = "Main"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch(Dispatchers.Default) {
val time = measureTime {
val one = async { firstThing()}
val two = async { secondThing()}
Log.d(TAG, "The answer is : ${one.await() + two.await()}")
}
Log.d(TAG, "Complete in: ${time} ms")
}
}
suspend fun firstThing() : Int {
delay(1000L)
return 10
}
suspend fun secondThing() : Int {
delay(1000L)
return 20
}
첫번째 코루틴이 실행이되고, 1초 동안 delay가 되고, 두번째 코루틴도 실행되며 1초 동안 delay가 됩니다.
await을 통해 코루틴이 실행되어 값을 반환하기 전까지 대기하고, 두 코루틴이 모두 실행되어 값을 반환하면 log값이 찍히게 됩니다.
'프로그래밍 > Android' 카테고리의 다른 글
Android Jetpack WorkManager와 Coroutine으로 특정 시간에 백그라운드 작업하기 (1) | 2024.01.14 |
---|---|
onBackPressed() deprecated 해결 (0) | 2024.01.13 |
Android JetPack Compose (0) | 2023.09.14 |
Android Architecture Components - ViewModel (0) | 2023.09.06 |
Android Architecture Components - LifeCycles (0) | 2023.08.18 |