[Coroutine] 코루틴(1)

Janzizu·2022년 9월 10일
0
post-thumbnail

간단한 코루틴

import kotlinx.coroutines.*

fun main() = runBlocking {
    println(Thread.currentThread().name)
    println("Hello")
}

main @coroutine#1
Hello

코루틴을 만드는 가장 간단한 함수는 runBlocking 이며, 이렇게 코루틴을 만드는 함수를 코루틴 빌더라고 한다.
runBlocking은 코루틴을 만들고 코드 블록이 수행이 끝날 때 까지 runBlocking의 다음 코드를 수행하지 못 하게 막는다. (blocking !)

코루틴 빌더의 수신 객체

import kotlinx.coroutines.*

fun main() = runBlocking {
    println(this)
    println(Thread.currentThread().name)
    println("Hello")
}

"coroutine#1":BlockingCoroutine{Active}@75412c2f
main @coroutine#1
Hello

runBlocking 안에서 this를 실행하면 코루틴이 수신 객체인 것을 알 수 있다.
println(this) 를 수행하면 위와 같은 결과가 나오는데,
BlockingCoroutine은 CoroutineScope의 자식이다.
코루틴을 쓰는 모든 곳에는 코루틴 스코프가 있다고 생각하면 된다.

코루틴 컨텍스트

import kotlinx.coroutines.*

fun main() = runBlocking {
  println(coroutineContext)
  println(Thread.currentThread().name)
  println("Hello")
}

[CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@f5f2bb7, BlockingEventLoop@73035e27]
main @coroutine#1
Hello

코루틴 스코프는 코루틴을 처리하기 위한 정보인 코루틴 컨텍스트를 가지고 있다.

launch 코루틴 빌더

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        println("launch: ${Thread.currentThread().name}")
        println("World!")
    }
    println("runBlocking: ${Thread.currentThread().name}")
    println("Hello")
}

runBlocking: main @coroutine#1
Hello
launch: main @coroutine#2
World!

launch는 코루틴 빌더이며, 새로운 코루틴을 만들기 때문에 새로운 코루틴 스코프를 만들게 된다.
launch => 할 수 있다면 다른 코루틴 코드를 같이 수행시키는 코루틴 빌더.

실행결과를 보면 launch 코루틴 빌더에 있는 내용이 runBlocking 코루틴 빌더에 있는 흐름보다 늦게 수행된 것을 알 수 있는데, 둘 다 메인 스레드를 사용하기때문에 runBlocking의 코드들이 메인 스레드를 다 사용할 때 까지 launch의 코드 블록이 기다리게 되는 것이다.
runBlocking은 Hello를 출력하고서 종료하지 않고, launch의 코드들이 다 끝날 때까지 기다린다.

delay 함수

fun main() = runBlocking {
    launch {
        println("launch: ${Thread.currentThread().name}")
        println("World!")
    }
    println("runBlocking:${Thread.currentThread().name}")
    delay(500L)
    println("Hello")
}

runBlocking:main @coroutine#1
launch: main @coroutine#2
World!
Hello

위와 마찬가지로 runBlocking 코드블럭 내용이 먼저 호출이 되는데, 스레드이름이 호출되고나서
delay에 의해 잠에들게되고, launch의 코드 블록이 먼저 수행된다.

코루틴 내에서 sleep

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        println("launch: ${Thread.currentThread().name}")
        println("World!")
    }
    println("runBlocking:${Thread.currentThread().name}")
    Thread.sleep(500)
    println("Hello")
}

runBlocking:main @coroutine#1
Hello
launch: main @coroutine#2
World!

Thread.sleep을 하면 코루틴이 아무 일을 하지 않는 동안에도 스레드를 양보하지 않고 독점한다.

여러 launch

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        println("launch1: ${Thread.currentThread().name}")
        delay(1000L)
        println("3!")
    }
    launch {
        println("launch2: ${Thread.currentThread().name}")
        println("1!")
    }
    println("runBlocking: ${Thread.currentThread().name}")
    delay(500L)
    println("2!")
}

runBlocking: main @coroutine#1
launch1: main @coroutine#2
launch2: main @coroutine#3
1!
2!
3!

딜레이 값을 바꾸면 출력 결과가 달라진다.

상위 코루틴 & 하위 코루틴

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        launch {
            println("launch1 :${Thread.currentThread().name}")
            delay(1000L)
            println("3!")
        }
        launch {
            println("launch2 : ${Thread.currentThread().name}")
            println("1!")
        }
        println("runBlocking: ${Thread.currentThread().name}")
        delay(500L)
        println("2!")
    }
    print("4!")
}

runBlocking: main @coroutine#1
launch1 :main @coroutine#2
launch2 : main @coroutine#3
1!
2!
3!
4!

runBlocking안에 두 launch가 속해 있는 것을 볼 수 있다.
runBlocking은 그 속에 포함된 launch가 끝나기 전에 종료되지 않는다.

suspend

import kotlinx.coroutines.*

suspend fun doThree() {
    println("launch1: ${Thread.currentThread().name}")
    delay(1000L)
    println("3!")
}

suspend fun doOne() {
    println("launch1: ${Thread.currentThread().name}")
    println("1!")
}

suspend fun doTwo() {
    println("runBlocking: ${Thread.currentThread().name}")
    delay(500L)
    println("2!")
}

fun main() = runBlocking {
    launch {
        doThree()
    }
    launch {
        doOne()
    }
    doTwo()
}

runBlocking: main @coroutine#1
launch1: main @coroutine#2
launch1: main @coroutine#3
1!
2!
3!

doOne은 delay와 같은 함수(지연함수,suspend)를 호출하기 않았기 때문에 일반 함수로 해도 된다. 만약 suspend를 다른 함수에서 호출하려면 그 함수가 suspend함수이거나 코루틴 빌더를 통해 코루틴을 만들어야 한다.

0개의 댓글