Coroutines이란?
개요
Kotlin을 사용하며 Coroutines를 사용하여 여러 동시처리를 하기 위해 공부해보고자 합니다.
Coroutine이란?
코투린은 루틴의 일종이며, Co는 with 또는 together를 뜻합니다.
Routine이란 하나의 task 또는 함수로 생각하면 됩니다.
이름만 보았을때는 코틀린에서 만들어진 개념같지만 코틀린의 것만이 아니며 여러 언어에서 지원하고 있는 개념입니다.
예를 들어 javascript의 async await를 사용한 경험이 있다면 이미 코루틴을 사용해 본 경험이 있는 것입니다.
코루틴은 실행을 일시 중단했다가 다시 시작할 수 있는 컴퓨터 프로그램 구성 요소로, 협력적 멀티태스킹을 위한 서브루틴을 일반화합니다. 코루틴은 협동 작업, 예외, 이벤트 루프, 반복자, 무한 목록 및 파이프와 같은 익숙한 프로그램 구성 요소를 구현하는 데 적합합니다.
async 또는 non-blocking 프로그래밍은 개발 분야에서 중요합니다.
기존에는 비동기 처리를 하기 위해 reactive programming을 사용하였으나 Kotlin의 코루틴이 등장하게 되었습니다.
코루틴의 3가지 키워드를 통해 더 쉽게 알아보겠습니다.
- 협력형 멀티 태스킹
- 동시성 프로그래밍 지원
- 비동기 처리를 쉽게
협력형 멀티 태스킹
우리에게 너무나 익숙한 코드로 Main 함수가 서브 함수인 plusOne을 호출합니다.
서브루틴에 진입하고 빠져나오는 시점이 매우 명확하며, return문을 만나거나 서브루틴의 닫는 괄호를 만나면 서브루틴을 빠져나오게 됩니다.
메인스레드는 plusOne이라는 서브루틴에 진입하고 빠져나오기까지(return 되기 전) 쓰레드는 blocking 되어 아무 일도 할 수 없습니다.
하지만 코루틴은 다릅니다.
코루틴도 하나의 routine이므로 함수로 생각하면 됩니다.
하지만 함수에 진입할 수 있는 지점도 여러 개고 탈출할 수 있는 지점도 여러 개입니다.
return이나 마지막 닫는 괄호를 만나지 않아도 언제든지 중간에 나가고 들어올 수 있습니다.
fun main() = runBlocking {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int{
println("doSomethingUsefulOne")
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int{
println("doSomethingUsefulTwo")
delay(1000L)
return 29
}
코드에 작성된 순서대로 doSomethingUsefulOne이 실행되고 doSomethingUsefulOne의 실행이 끝난 후 doSomethingUsefulTwo가 실행됨을 알 수 있습니다.
그렇다면 하나의 코루틴 안에서 두 함수를 비동기 처리하고 싶으면 어떻게 해야 할까요? async과 await 키워드를 이용하면 됩니다.
함수 앞에 async를 붙여 비동기 처리를 명시하고 await를 사용하여 해당 함수가 처리될 때까지 기다려 결괏값을 얻을 수 있습니다.
다음과 같이 코드를 작성할 수 있습니다.
fun main() = runBlocking {
val time = measureTimeMillis {
val one = async{doSomethingUsefulOne()}
val two = async{doSomethingUsefulTwo()}
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int{
println("doSomethingUsefulOne")
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int{
println("doSomethingUsefulTwo")
delay(1000L)
return 29
}
코드를 실행해 보면 두 함수가 비동기적으로 처리되었기 때문에 약 1000ms 동안 코드가 실행될 것이라 예상할 수 있습니다.
동시성 프로그래밍
병렬성이 아닌 동시성을 지원하는 개념입니다.
보통 동시성을 보장하기 위해서는 스레드를 사용하거나, 코루틴을 사용합니다.
흔히 코루틴은 경량스레드로 불리며 Context Switching 비용을 대폭 줄였습니다.
위의 예제가 2초가 걸리는 일을 1초가 걸리도록 하는 동시성을 지원하는 예시입니다.
비동기 처리
흔히 비동기를 처리하기 위해 callback을 사용하면 callback지옥이라는 말이 같이 보입니다.
RxJava, RxKotlin을 활용하면 훨씬 간편해지지만 학습곡선이 꽤 높습니다.
코루틴을 활용하면 언제 끝날지 모르는 비동기 작업들의 순서가 명확하게 지켜집니다.
참고자료
https://www.youtube.com/watch?v=Vs34wiuJMYk
https://kotlinlang.org/docs/coroutines-overview.html
https://kotlinlang.org/docs/coroutines-guide.html