코딩하는 개굴이

코드로 Coroutine 동작 파악하기 본문

안드로이드/KOTLIN

코드로 Coroutine 동작 파악하기

개굴이모자 2023. 9. 17. 23:37
반응형
해당 포스팅은 Fastcampus 의 실무 프로젝트로 배우는 Kotlin & Spring: 리팩토링부터 서비스 구현까지 강의를 기반으로 작성되었습니다.

 

Coroutine 의 기초 동작을 파악해볼 수 있도록 runBlocking, launch, async, suspend, flow 등을 사용해 코드를 작성해 보았다.

실행해보며, 감을 잡아보도록 하자 :)

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlin.system.measureTimeMillis

fun main() {

    // ##################### RunBlocking #####################
    // 일반적으로 코루틴은 스레드를 차단하지 않지만, runblocking 은 차단하여 순차적으로 동작하게 한다.
    // 특정 라이브러리나 프레임워크에서 코루틴을 지원하지 않는다거나, 테스트 코드를 작성한다거나 등의 상황에서는 꼭 사용해야하는 경우에만 사용한다.

    runBlocking {
        println("Hello")
        println(Thread.currentThread().name)
    } //이 코드가 완료되면 다음 라인의 코드가 진행된다.

    println("World")
    println(Thread.currentThread().name)

    /** [결과]
     * Hello
     * main @coroutine#1
     * World
     * main
     * */

    // ##################### launch #####################
    // 결과로 job 을 반환하는 코루틴

    runBlocking <Unit> {
        println("=======================")
        launch {
            delay(500) //코루틴 라이브러에서 정의된 일시 중지 메서드로, 스레드를 차단하지 않고 중단한다.
            //Thread.sleep(500) //스레드를 아예 차단해 블로킹해버린다.
            println("World!")
        }
        println("Hello")
    }
    /** [결과]
     * Hello
     * World
     * */

    runBlocking<Unit> {
        println("=======================")
        val job1 = launch {
            val elapsedTime = measureTimeMillis {
                delay(150)
            }
            println("async task - 1 $elapsedTime ms")
        }
        job1.cancel()

        val job2 = launch(start = CoroutineStart.LAZY) {
            //실제로 start 를 하는 시점에 실행되게되며 start 를 하지 않으면 아예 실해되지 않는다.
            val elapsedTime = measureTimeMillis {
                delay(200)
            }
            println("async task - 2 $elapsedTime ms")
        }

        println("job2 is started")
        job2.start()

        /** [결과]
         * job2 is started
         * async task - 2 205 ms
         * */
    }

    // ##################### async #####################
    fun sum(a: Int, b: Int) = a + b
    runBlocking {
        println("=======================")
        val result1 = async {//비동기 처리를 할 때 async 를 사용한다.
            delay(100)
            sum(1, 3)
        }
        println("result1: ${result1.await()}") //await 을 걸어 비동기 결과를 기다린다.

        val result2: Deferred<Int> = async {//Deferred 라는 타입으로 결과를 묶는데, 여기서 await 함수를 제공한다.
            delay(50)
            sum(7, 3)
        }
        println("result2: ${result2.await()}")

        /** [결과]
         * result1: 4
         * result2: 10
         * */
    }

    // ##################### suspend #####################
    // suspend 함수를 직접 호출하고자 한다면 runblocking 혹은 본인 또한 suspend 여야 가능하다.

    fun printHello() = println("Hello ")
    suspend fun suspendTest() = coroutineScope { //coroutineScope 는 runBlocking 과 달리 블로킹되지 않고 코루틴이 동작된다.
        launch {
            delay(200)
            println("world!")
        }

        launch {
            printHello()
        }
    }

    runBlocking {
        println("=======================")
        suspendTest()

        /** [결과]
         * Hello
         * world!
         * */
    }

    // ##################### Flow #####################
    fun simple(): Flow<Int> = flow {
        println("Flow started")

        for (i in 1..3) {
            delay(100)
            emit(i) // emit 을 통해 데이터를 통제한다.
        }
    }

    runBlocking {
        println("=======================")
        val flow = simple()
        flow.collect { //subscribe 처럼 터미널 오퍼레이터로 collect 를 제공한다.
            println(it)
        }

        /** [결과]
         * 1
         * 2
         * 3
         * */
    }
}
반응형
Comments