💡 코루틴(Coroutine)이란?
- 프로세스 = 실행중인 프로그램, 실행 코드가 메모리에 올라가면 프로세스가 시작된다
- 여러개의 프로그램을 사용중이라면 프로세스도 여러개
- 스레드 = 하나의 프로세스 안에 존재하는 여러개의 실행 단위이다
- 스레드가 꼴랑 하나밖에 없단면 프로그램은 너무 힘들 것이다. 그래서 CPU가 여러개인 것도 이런 부분을 커버해주기 위해서 여러개의 스레드를 만들어 여러 수행을 동시에 진행하는 것을 도와주는 것이다
- 코루틴 = 스레드 안에 존재하는 작업 단위 (스레드의 스레드, 경량 스레드 라고 부르기도 한다)
안드로에드에서 가장 중요한 스레드는 메인 스레드 로 UI 를 그려주고 사용자가 화면과 상호작용할 수 있도록 해주는 스레드이다. 그런데 이 메인 스레드가 너무 바쁘면 메인 스레드가 블로킹 되고 상호작용을 못하게 되면서 앱이 강제 종료되는 ANR 에러가 발생한다.
그래서 무거운 작업은 다른 스레드를 만들어 수행해줘야 한다. 이렇게 여러 스레드를 만들어 관리해주기 위해 다양한 방법이 존재한다.
Thread 를 만들고 관리하는 방법
1. Thread 클래스를 상속받아 사용
- 스레드, 핸들러 개념까지 엮어서 가장 기초적으로 배우는 방법인 것 같다.
- Thread() 를 상속받아 클래스를 만들고, run() 메서드를 오버라이딩 해 그 안에 수행 할 내용을 적어주면 된다.
- 이렇게 생성된 스레드가 많은 메모리를 차지하면서 재사용이 어려운 단점이 있다. 또한 스레드를 개발자가 직접 관리해야 하기 때문에 메모리 누수의 가능성이 올라간다. (관리라고 하는 것은 스레드를 생성, 삭제, 재사용 이런것들을 말하는 것이다)
fun main() {
val exampleThread = ExampleThread()
exampleThread.start()
}
class ExampleThread : Thread() {
override fun run() {
println("[${Thread.currentThread().name}] New Thread Running")
}
}
2. Executor 프레임워크를 사용하는 방법
- 개발자가 스레드를 관리해야 한다는 책임과 부담을 낮춰주고, 재사용성을 높여준다.
- Executor 프레임워크가 스레드의 집합체인 ‘스레드 풀’ 을 만들고 작업을 제출하면 스레드 풀의 스레드 중 하나에 작업을 할당한다고 한다. 즉, 알아서 스레드 풀에 여유있는 스레드에 작업을 할당하고, 스레드를 재사용하나 보다.
fun main() {
// ExecutorService 생성
val executorService: ExecutorService = Executors.newFixedThreadPool(4)
// 작업 제출
executorService.submit {
println("[${Thread.currentThread().name}] 새로운 작업1 시작")
}
// 작업 제출
executorService.submit {
println("[${Thread.currentThread().name}] 새로운 작업2 시작")
}
// ExecutorService 종료
executorService.shutdown()
}
/*
출력
[pool-1-thread-1] 새로운 작업1 시작
[pool-1-thread-2] 새로운 작업2 시작
*/
3. Rx 라이브러리를 사용하는 방식
- 솔직히 Rx, RxJava 이런것들을 몰라서 잘 모르겠다
위 3가지 방법 모두 메인 스레드 외의 다른 스레드들을 만들어 작업을 수행하는 방식이다.
그런데 이렇게 스레드를 여러개 만들어 내는것, 그리고 여러 스레드가 전환되면서 여러 작업이 이것저것 수행되는 것(하나의 스레드만 계속 수행되는게 아니라, 여러 스레드가 빠른속도로 번갈아 가면서 수행되기 때문에 우리가 여러 작업을 동시에 처리하는 것 같은 효과를 얻을 수 있는 것이다) 은 매우 비용이 많이 든다.
또한 A 스레드에서 작업 수행 중 B 스레드의 수행 결과를 기다려야 하는 경우, 기다리는 동안 A스레드는 꼼짝없이 blocking 되어있는 상황에 처한다.
그래서 나온 기술이 코루틴 이다!!
코루틴은 매번 새로운 스레드를 만드는 것이 아니라 스레드 안에 여러개의 작업 단위(=코루틴)를 만들어 사용한다.
그래서 아래 그림처럼 작업1을 수행하는 코루틴1이 스레드1에서 실행되고, 중간에 작업2의 결과를 기다리는 동안 또다른 작업단위인 코루틴3이 스레드1에서 실행될 수 있다.
여기서 코루틴1은 작업2를 기다리는 동안 자신의 작업을 일시중단 하고 코루틴3에게 스레드를 양보한다.
또한 ‘조세영의 Kotlin World’를 통해 공부하면서 새롭게 알게 된 사실이 있다…!
난 코루틴을 사용하기 위해 항상 라이브러리를 추가해서 사용해왔다.
하지만 코루틴은 코틀린 언어에 기본적으로 내장되어 있는 기능이다. 따라서 별도의 라이브러리 설정 없이도 사용 가능하지만 그것은 저수준 API 라는 것이다. 그리고 UI 가 있는 앱의 경우 Main 스레드가 중요하다고 했는데 이 Main 스레드를 코루틴에서 사용하기 위해 필요한 라이브러리도 따로 있었다.
// 고수준 코루틴을 사용하기 위한 라이브러리
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
// 코루틴에서 Main 스레드를 사용하기 위한 라이브러리
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
'Android > Coroutine' 카테고리의 다른 글
[안드로이드/Coroutine] Coroutine 예제를 통해 본격 딥다이브 & Structured concurrency (0) | 2024.07.18 |
---|---|
[안드로이드/Coroutine] Coroutine Dispatcher (0) | 2024.07.18 |