카테고리 없음

Coroutine 이해하기 4

WOO_JOO 2022. 12. 26. 01:02

이번엔 Coroutine의 async에 대해서 알아보겠습니다.

 

지금까지 Coroutine의 예제에서는 프로그램의 순서에 따라 혹은 job을 컨트롤한 순서에 따라 실행되었습니다.

하지만 async 키워드를 사용하면 코드를 "동시에 " 수행 할 수 있습니다.

 

결과를 쉽게 보기위해서 어떻게 하면 좋을까 고민해봤는데, 그냥 시간으로 출력하는게 가장 한눈에 보기 쉬울 것 같습니다.

아 그리고 async는 Deferred 객체를 반환합니다. launch가 Job을 반환하는 것 처럼요.

 

kotlin에서는 measureTimeMillis{} 키워드를 사용하면 해당 블럭내 코드가 수행한 시간을 리턴해 줍니다.

이를 활용해서 async 키워드를 쓰고 안 쓰고의 차이를 확인해 보겠습니다.

 

suspend fun getRandomNumber1(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

suspend fun getRandomNumber2(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

fun main() = runBlocking {
    val elapsedTime = measureTimeMillis {
        val random1 = getRandomNumber1()
        val random2 = getRandomNumber2()
        println("${random1} + ${random2} = ${random1 + random2}")
    }
    println(elapsedTime)
}

결과
135 + 97 = 232
2028

getRandomNumber1()과 getRandomNumber2() 각각 코드를 수행했을때 1초정도 딜레이 시켰습니다.

그래서 결과는 2초하고 약간 더 나오네요.

 

 

suspend fun getRandomNumber1(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

suspend fun getRandomNumber2(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

fun main() = runBlocking {
    val elapsedTime = measureTimeMillis {
        val number1 = async { getRandomNumber1() }
        val number2 = async { getRandomNumber2() }
        println("${number1.await()} + ${number2.await()} = ${number1.await() + number2.await()}")
    }
    println(elapsedTime)
}
//결과
248 + 227 = 475
1050

시간차이가 보이시나요?

 

async 키워드를 사용하면 해당 코루틴 블럭이 동시에 수행되는 것을 알 수 있습니다.

사실 async 키워드는 저희가 앞서 봤던 launch와 비슷하게 볼 수는 있습니다만, 차이는 어느정도 있습니다.

첫번째 차이는 async는 결과값을 리턴할 수 있습니다.

결과를 받아야한다면 "async" 아니면 "launch"

두번째로는 그리고 async는 await() 함수를 사용할 수 있습니다.

await의 역할은 async가 리턴한 값을 받아 올수 있고 또 추가로, 작업이 끝날때까지 코루틴이 보장해줍니다. 그러니까 job.join()의 역할 하는거죠.

job.join() 복습하자면 해당 launch블록내에 코드가 끝까지 수행될때 까지 다른 코루틴에게 자원을 양보하지 않고 기다려줍니다. delay함수가 있어도 말이죠.

await()은 job.join()과 async의 리턴값 받기 까지 가능 합니다.

 

 

그리고 async는 LAZY 키워드를 사용할 수 있습니다.

suspend fun getRandomNumber1(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

suspend fun getRandomNumber2(): Int {
    delay(1000L)
    return Random.nextInt(0, 500)
}

fun main() = runBlocking {
    val elapsedTime = measureTimeMillis {
        val value1 = async(start = CoroutineStart.LAZY) { getRandomNumber1() }
        val value2 = async(start = CoroutineStart.LAZY) { getRandomNumber2() }

        value1.start()
        value2.start()

        println("${value1.await()} + ${value2.await()} = ${value1.await() + value2.await()}")
    }
    println(elapsedTime)
}


// 실행결과
237 + 5 = 242
1038

CoroutineStart.LAZY를 사용하면 지금당장 코드를 수행하는 것이 아닌 대기상태에 들어가게됩니다.

 

작업을 수행할 Queue에 올라가서 대기 하게되고 start하는 시점에 코드가 수행하게 됩니다.