# Coroutine 개념
# Coroutine
코루틴을 배워보기 전에, 알고가면 좋을 개념들이다.
# CoroutineScope, GlobalScope
CoroutineScope
는 말 그대로 코루틴을 묶음으로 제어할수 있는 단위다.
GloablScope는 CoroutineScope의 한 종류이며 프로그램 전반에 걸쳐 백그라운드에서 실행된다.
# CoroutineContext
CoroutineContext
는 코루틴을 어떻게 처리할것인지 에 대한 레퍼런스이며
주요 요소로는 Job, dispatcher가 있다.
# Dispatcher
Dispatcher
는 CoroutineContext의 주요 요소다.
CoroutineContext 을 상속받아 어떤 스레드를 이용해서 어떻게 동작할것인지를 미리 정의되어있다.
- Dispatchers.Default : CPU 사용량이 많은 작업에 사용, 주 스레드에서 작업하기에는 너무 긴 작업들에게 적합.
- Dispatchers.IO : 네트워크, 디스크 사용 할때 , 파일 및 소켓 입출력 작업에 최적화.
- Dispatchers.Main : 안드로이드의 경우 UI 스레드를 사용.
- Dispatchers.Unconfined : 특정 스레드, 스레드 풀을 지정하지 않으며 일반적으로 사용 안함.
# launch, async
launch
, async
는 CouroutineScope의 확장함수 이며, 넘겨받은 코드블록으로 코루틴을 만들고 실행해주는 빌더다.
launch
는 Job, async
는 Defferd 객체를 반환하며, 이 객체를 사용해 수행 결과를 받거나 스케쥴 관리가 가능하다.
# launch
launch()로 정의된 코루틴 블록은 Job을 반환한다.
val job : Job = launch {
...
}
받아온 Job객체는 해당 코루틴 블록의 스케쥴 관리나 상태 등을 확인 할 떄 사용한다.
Job.join()을 이용하면 해당 코루틴 블록이 완료 될 떄 까지 대기 할 수 있다.
fun main() = runBlocking {
val job1: Job = launch {
var i = 0
while (i < 10) {
delay(500)
i++
}
}
//job1 완료 대기
job1.join()
//job1.cancel() 취소
}
job객체가 여러개라면 일일이 join()을 호출하지 않고 joinAll()을 사용할 수 있다.
joinAll(job1,job2)
launch()로 정의된 코루틴 블록은 즉시 수행되며 Job 객체로는 블록 제어는 가능하지만 블록의 결과를 받아 올 수없다. 이를 위해서는 async()를 사용하면 된다.
# async
async()로 정의된 코루틴 블록은 Defferd을 반환한다.
val deferred : Deferred<T> = async {
...
T // 결과값
}
받아온 Defferd객체를 이용해 제어가 가능하며 await()를 이용해 결과값을 받아올 수 있다.
fun main() = runBlocking {
val deferred: Deferred<String> = async {
var i = 0
while (i < 10) {
delay(500)
i++
}
"result"
}
val msg = deferred.await()
println(msg) // result 출력
}
여러개의 async 코루틴 블록을 실행할 경우 각각의 Deferred 객체에 대해서 await() 함수로 코루틴 블록이 완료 될때까지 다음 코드 수행을 대기할수 있다. await() 함수는 코루틴 블록이 완료되면 결과를 반환한다.
fun main() = runBlocking {
val deferred1 = async {
var i = 0
while (i < 10) {
delay(500)
i++
}
"result1"
}
val deferred2 = async {
var i = 0
while (i < 10) {
delay(1000)
i++
}
"result2"
}
val result1 = deferred1.await()
val result2 = deferred2.await()
println("$result1 , $result2") // result1 , result 2 출력
}
물론 join()과 마찬가지로 await()도 한번에 호출할 수 있다.
var result : List<Any> = awaitAll(deferred1,deferred2)
Any 타입으로 결과를 받기 때문에 반환형태가 달라도 상관없다.
fun main() = runBlocking {
val deferred1 = async {
var i = 0
while (i < 10) {
delay(50)
i++
}
"result1"
}
val deferred2 = async {
var i = 0
while (i < 10) {
delay(10)
i++
}
1234
}
val result1 = deferred1.await()
val result2 = deferred2.await()
awaitAll(deferred1,deferred2).forEach{
println(it)
}
}
# 지연실행
launch() 와 async() 모두 즉시 실행되는데 만약 처리 시점을 뒤로 미루고 싶을 경우 CoroutineStart.LAZY를 이용해 지연 시킬 수 있다.
val job = launch (start = CoroutineStart.LAZY) {
...
}
또는
val deferred = async (start = CoroutineStart.LAZY) {
...
}
launch 코루틴 블록을 지연 실행 시킬 경우 Job 클래스 의 start() 함수 를 호출하거나 join() 함수를 호출하는 시점에 launch 코드 블록이 수행된다.
job.start()
또는
job.join()
async 코루틴 블록을 지연 실행 시킬 경우 Deferred 클래스 의 start() 함수 를 호출하거나 await() 함수를 호출하는 시점에 async 코드 블록이 수행된다.
deferred.start()
또는
deferred.await()
launch, async 블록 모두 start()로 실행시킬 경우 수행결과를 반환하지 않는다.
fun main() = runBlocking {
println("start")
val deferred = async(start = CoroutineStart.LAZY) {
var i = 0
while (i < 5) {
delay(500)
println("lazy async $i")
i++
}
}
deferred.start()
println("end")
}
위에처럼 start()로 실행 할 경우
start
end
lazy async 0
lazy async 1
lazy async 2
lazy async 3
lazy async 4
를 출력하게 된다.
← 시작하기 Coroutine 기초 - 1 →