[Android/Kotlin] Coroutine이란?
![[Android/Kotlin] Coroutine이란?](/assets/img/kotlin_img/kotlin.png)
- 오늘은 Coroutine에 대해서 알아보려고 한다.
- 기능
- Coroutine이란?
- CoroutineScope란?
- Coroutine Dispatcher란?
- Coroutine 상태관리 메서드
- Coroutine suspend 함수
- 코루틴 withContext()
- Coroutine 빌더
오늘은 Coroutine에 대해서 알아보려고 한다.
- Coroutine은 co와 routine이 합쳐진 단어로서 해석 해보자면 특정한 일을 실행하기 위한 함께 작동하는 프로그램의 일부라고 해석이 된다.
- 아직까지는 확실히 감이 잘 오지 않기 때문에 Developer와 Kotlin공홈의 설명을 확인해보자.
[ Developer : Kotlin Coroutine ]
코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다. 코루틴은 Kotlin 버전 1.3에 추가되었으며 다른 언어에서 확립된 개념을 기반으로 합니다.
Android에서 코루틴은 기본 스레드를 차단하여 앱이 응답하지 않게 만들 수도 있는 장기 실행 작업을 관리하는 데 도움이 됩니다. 코루틴을 사용하는 전문 개발자 중 50% 이상이 생산성이 향상되었다고 보고했습니다.
기능
코루틴은 Android의 비동기 프로그래밍에 권장되는 솔루션입니다. 주목할 만한 기능은 다음과 같습니다.
- 경량: 코루틴을 실행 중인 스레드를 차단하지 않는 정지를 지원하므로 단일 스레드에서 많은 코루틴을 실행할 수 있습니다. 정지는 많은 동시 작업을 지원하면서도 차단보다 메모리를 절약합니다.
- 메모리 누수 감소: 구조화된 동시 실행을 사용하여 범위 내에서 작업을 실행합니다.
- 기본으로 제공되는 취소 지원: 실행 중인 코루틴 계층 구조를 통해 자동으로 취소가 전달됩니다.
- Jetpack 통합: 많은 Jetpack 라이브러리에 코루틴을 완전히 지원하는 확장 프로그램이 포함되어 있습니다. 일부 라이브러리는 구조화된 동시 실행에 사용할 수 있는 자체 코루틴 범위도 제공합니다.
[ Kotlin : Coroutines basics ]
Coroutine은 일시 중단 가능한 계산의 인스턴스입니다. 이는 나머지 코드와 동시에 작동하는 코드 블록을 실행해야 한다는 점에서 개념적으로 스레드와 유사합니다. 그러나 Coroutine은 특정 스레드에 바인딩되지 않습니다. 한 스레드에서 실행을 일시 중단하고 다른 스레드에서 다시 시작할 수 있습니다. Coroutine은 경량 스레드로 생각할 수 있지만 실제 사용법이 스레드와 매우 다르게 만드는 중요한 차이점이 많이 있습니다.
위의 내용을 바탕으로 정리를 해보자
Coroutine이란?
일시 중단이 가능한 계산의 인스턴스이다.- 나머지 코드와 동시에 작동하는 코드 블록을 실행해야 한다는 점에서 스레드와 유사하지만, Coroutine은 특정 스레드에 바인딩 되지 않는다는 차이점이 있다.
한 스레드에서 실행을
정지하고 다른 스레드에서재개할 수 있다.비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴이다.
기본 스레드를 차단하여 앱이 응답하지 않게 만들 수도 있는 장기 실행 작업을 관리하는 데 도움이 된다.
생산성 향상에 도움이 된다.
코루틴의 장점 중에 하나는 비동기적으로 실행된다는 점이다.
- 동기적(Synchronous) : 어떠한 작업을 요청했을 때 그 작업을 끝내고 다음 작업을 실행함
- 비동기적(Asynchronous) : 어떠한 작업을 요청했을 때 그 작업이 끝날 때 까지 기다리지 않고 다른 작업을 하다가 요청했던 작업이 끝나면 중단됐던 작업을 다시 수행함
기능
- 경량 : 실행중인 스레드를 차단하지 않는
정지를 지원- 단일 스레드에서 많은 코루틴 실행 가능
- 많은 동시작업 지원, 메모리 절약
- 메모리 누수 감소 : 구조화된 동시 실행을 사용하여 범위 내의 작업 실행
- 취소 지원 : 실행중인 코루틴 계층 구조를 통해 자동으로 취소 전달
- Jetpack 통합 : Jetpack 라이브러리와의 호환성 좋음
- 추가적으로 공홈에 나와있는 코드를 통해 코루틴을 알아가는 Step을 밟아보자.
fun main () = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
}
출력값
Hello
World!
위의 코드를 분석해보자
launch는 비동기 작업을 수행하는데 사용되는 코루틴 빌더이다.- 나머지 코드와 동시에 새로운 코루틴을 시작하며 독립적으로 작동한다.
- 따라서
Hello가 먼저 출력된다.
Delay는 특별한 중단 함수로, 일정시간동안 코루틴을일시중단한다.- 코루틴을 중단시키지만 기본 스레드를 차단하지 않고 다른 코루틴이 실행되고 기본 스레드를 활용할 수 있게 한다.
runBlocking은 일반적인fun main()과 코루틴 내부의 코드를 연결하는 데 사용되는 코루틴 빌더이다.runBlocking블록 내에서 코루틴이 실행되며, 이는 IDE에서 CoroutineScope 힌트로 강조 표시된다.
만약 코드에서 runBlocking을 제거하거나 빠트리면 launch 호출에서 오류가 발생한다. 왜냐하면 launch는 CoroutineScope에서만 선언되기 때문이다.
CoroutineScope란?
CoroutineScope는 코루틴을 실행하고 제어하는 데 사용되는 인터페이스이다. 코루틴은 비동기적인 작업을 처리하는 데 유용하며, CoroutineScope는 이러한 코루틴을 조직화하고 관리하기 위한 일종의 컨테이너 역할을 한다.
- 글로벌 스코프(GlobalScope):
앱의 생명주기와 함께 동작하므로 생명주기를 따로 관리해 줄 필요가 없다.앱이 시작할 때 부터 끝날 때 까지 긴 시간 실행되는 코루틴에 적합하다. - 코루틴 스코프(CoroutineScope): 서버에서 이미지를 가져오는 경우 같은 코루틴이 필요할 때만 사용하고 닫아주는 경우에 적합하다.
Coroutine Dispatcher란?
디스패처는 코루틴을 적당한 스레드에 할당하고 코루틴이 정지하거나 다시 실행하는 것을 담당한다.
Dispatcher의 종류
- Dispatchers.Main : 메인 스레드에서 코루틴을 실행한다.
UI와 상호작용에 최적화되어있고빠른 작업을 실행할 때 사용한다.- suspend 함수를 호출하고 안드로이드 UI 프레임워크 작업을 실행하며 LiveData 객체를 업데이트한다
- Dispatchers.Default : 메인 스레드 밖에서 코루틴을 실행한다.
CPU를 많이 쓰는 데이터 정렬이나복잡한 연산 같은 작업에 최적화 되어있다. - Dispatchers.IO : 메인 스레드 밖에서 코루틴을 실행한다.
이미지 다운로드, 파일 입출력 등 네트워크, 디스크, DB작업에 최적화 되어있다. - Dispatchers.Unconfined : 호출한 context 를 기본으로 사용하는데 중단하고 다시 실행될 때 context가 바뀌면 바뀐 context로 따라간다.
Coroutine 상태관리 메서드
- cancel : 코루틴의 동작을 멈추게 하는 method이다. 하나의 스포크 안에 여러개의 코루틴이 존재할 때 하위 코루틴까지 모두 멈춘다.
- join : 코루틴 내부에 여러 launch 블록이 있을 경우 모두 새로운 코루틴으로 분기되어 동시에 실행되기 때문에 순서를 정할 수 없다. 순서를 정해야할때 join method 를 이용해서 코루틴이 순차적으로 실행할 수 있도록 할 수 있다.
Coroutine suspend 함수
suspend는 현재 코루틴 실행을 일시중지하고 모든 로컬 변수를 저장하기 때문에 스레드가 멈추지 않는 효율적인 스레드 활용을 할 수 있게 된다.
코루틴이 멈출 때 코틀린 런타임은 해당 코루틴이 실행되던 스레드에 다른 코루틴을 할당해 실행시킨다.
멈춰있던 코루틴이 재개 될 때 사용 가능한 스레드에 할당해준다.
코루틴은 멈추면서 해당 루틴 상태를 저장하고 서브 루틴을 실행한 다음 저장한 부모루틴을 복원하는 방법으로 스레드에 영향을 주지 않는다.
코루틴 withContext()
suspend 함수를 코루틴 스코프에서 사용할 때 호출한 스코프와 다른 디스패쳐를 사용하는 경우가 있다.
- Kotlin은 가능한 한 스레드 전환을 방지하도록
Dispatchers.Default와Dispatchers.IO간의 전환을 최적화한다. - 따라서,
withContext()를 사용하여 스레드의 전환을 방지할 수 있다. - 다음은
withContext()를 사용하여 디스패처를 전환하는 코드이다.
CoroutineScope(Dispatchers.Default).launch{ // Default 디스패쳐 사용
val result = withContext(Dispatchers.IO){ // IO 디스패쳐로 변경
run()
}
}
Coroutine 빌더
- launch : 비동기적으로 코루틴을 실행한다. 호출자에게 반환값이 없으며, 해당 코루틴이 완료될 때까지 대기하지 않는다.
- 비동기적으로 작업을 실행하고 결과가 필요없는 경우에 적합하다.
- async : 비동기적으로 코루틴을 실행하며, 결과를 반환한다.
Deferred를 반환하며, 결과에 접근하기 위해await()함수를 사용하여 결과값을 얻을 때까지 대기한다.- 비동기 작업을 실행하고 결과값이 필요한 경우 사용한다.
suspend함수 내부에서만 사용 가능하다.
- withContext : 부모 코루틴에 의해 사용되던 context와 다른 context에서 코루틴을 실행 할 수 있다.
- coroutineScope : 다수의 코루틴을 사용하는 경우,
suspend함수가 시작하고 모든 코루틴이 완료될 때만 처리가 필요할 때 사용한다.- 여러 코루틴 중에서 하나라도 실패하면 모든 코루틴이 취소된다.
- supervisorScope :
coroutineScope와 비슷하지만 여러 코루틴 중에서 하나가 실패해도 다른 코루틴이 취소되지 않는다. - runBlocking : 블록 내에서 코루틴을 실행하고 완료될 때까지 현재 스레드를 차단한다. 일반적으로
main함수에서 사용된다.- 애플리케이션의 진입점에서 비동기 코드를 시작하고 관리하는 데 사용한다.
코루틴 suspend function
- withTimeout : 코루틴이 정해진 시간 안에 실행되지 않으면 예외를 발생시킨다.
- withTimeoutOrNull : 정해진 시간 안에 실행되지 않으면 null을 반환한다.
- awaitAll : 모든 작업의 성공을 기다리면서 작업중에 하나라도 실패하면 awaitAll 또한 실패한다.
- joinAll : 모든 작업이 끝날 때 까지 현재 작업을 일시 중단시킨다.