스코프 빌더 #1
목차
runBlocking
코루틴을 만드는 가장 간단한 함수. (코루틴 빌더라고 함)
코루틴을 만들고 코드 블록의 수행이 끝날 때까지 runBlocking 다음 코드를 수행하지 못하게 막음.
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
println(Thread.currentThread().name)
println("Hello")
}
코루틴 빌더의 수신 객체
runBlocking안에서 this를 수행하면 코루틴이 수신 객체(Receiver)인 것을 알 수 있음.
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
println(this)
println(Thread.currentThread().name)
println("Hello")
}
BlockingCoroutine{Active}@2ff5659e 이런 형태의 결과가 나옴.
BlockingCoroutine은 CoroutineScope의 자식이다. 코틀린 코루틴을 쓰는 모든 곳에는 코루틴 스코프가 있다고 생각하면 됨.
코루틴 컨텍스트
코루틴 스코프는 코루틴을 제대로 처리하기 위한 정보, 코루틴 컨텍스트를 가지고 있음.
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
println(coroutineContext)
println(Thread.currentThread().name)
println("Hello")
}
launch 코루틴 빌더
launch는 코루틴 빌더임.
launch는 "할 수 있다면 다른 코루틴 코드를 같이 수행"시키는 코루틴 빌더.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("launch: ${Thread.currentThread().name}")
println("World!")
}
println("runBlocking: ${Thread.currentThread().name}")
println("Hello")
}
launch 안에 있는 내용이 runBlocking 안에 있는 내용 보다 늦게 수행 됨.
메인 스레드를 다 사용할 때 까지 launch의 코드 블록이 기다림.
delay 함수
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("launch: ${Thread.currentThread().name}")
println("World!")
}
println("runBlocking: ${Thread.currentThread().name}")
delay(500L)
println("Hello")
}
delay가 호출이 되면 runBlocking의 코드가 쉬어가면서 500ms 동안 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")
}
Thread.sleep 은 delay 스레드가 중단되기 때문에 스레드를 양보하지 않음.
한번에 여러 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!")
}
중단 이후 깨어나는 순서에 따라 결과가 달라짐.
상위 코루틴은 하위 코루틴을 끝까지 책임짐
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 안에 두 launch 가 속해 있는데 계층화 되어 있어 구조적.
launch가 모두 끝나기 전까지 종료되지 않음.
suspend 함수
코드의 일부를 함수로 분리할 때 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()
}
doOne 함수는 delay와 같은 suspend 함수를 사용하지 않았기 때문에 suspend 생략 가능.
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
// if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
if (timeMillis < Long.MAX_VALUE) {
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
}