코루틴
💡지난 코루틴에 대한 설명에 이어 코루틴이 코드 속에서 어떻게 쓰이는지 추가 설명을 해보겠다!
IntelliJ에서 코루틴에 진입하는 방법은 runBlocking을 이용하는 것이다.
runBlocking은 CoroutineScope이기 때문에 launch를 실행할때 GlobalScope가 필요없다.
import kotlinx.coroutines.*
fun main() {
runBlocking {
launch {
delay(2000)
println("Hello")
}
launch {
delay(1000)
println("World!")
}
}
}
이렇게 코드를 짜면 출력값은
World!
Hello
가 나오게 된다.
World!가 먼저 출력되는 이유는 두 코루틴 작업이 병렬로 처리됐기 때문이다.
그렇다면 suspend function이란 무엇일까?
suspend function은 코드가 blocked 된 상태에 놓을 때, 그 작업을 중단하고 그 기간 동안 스레드에서 다른 작업을 수행할 수 있게 해준다.
import kotlinx.coroutines.*
fun main() {
runBlocking {
launch {
delay(2000)
println("Hello")
}
doSomething()
launch {
delay(1000)
println("World")
}
}
}
suspend fun doSomething() = coroutineScope{
launch {
delay(3000)
println("stop")
}
println("doSomething")
}
위의 코드는
doSomething
Hello
stop
World
순으로 출력된다.
그런데 두 가지 수행이 반환하는 값 사이에 아무런 상관관계가 없어서 동시에 호출을 해도 되는 상황이라면 async를 사용한다.
async는 각각 분리된 새로운 코루틴을 생성하여 다른 코루틴들과 동시에 동작하게 해준다.
async는 Deferred을 반환해준다. Deferred란, 코루틴 스코프 내에 정의된 모든 동작을 수행한 뒤에 언젠가 어떤 결과를 꼭 제공해주겠다는 약속을 해준다.
결과값은 await() 함수를 통해 접근할 수 있다.
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Default
private suspend fun getStock1() : Int{
delay(10000)
return 55000
}
private suspend fun getStock2() : Int{
delay(8000)
return 35000
}
fun main() {
runBlocking {
val stock1 = async(Default) {
getStock1()
}
val stock2 = async(Default) {
getStock2()
}
val total = stock1.await() + stock2.await()
println("Total: $total")
}
}
Channel
Channel은 지난 블로그에서 코루틴의 값을 받을 수 있는 역할이라고 했다. 더 추가적인 설명을 하자면 Channel은 Queue와 비슷한 기능을 한다. FIFO구조로 제공되며, 먼저 receive를 호출하는 Coroutine이 원소를 갖게 된다.
send를 이용하여 값을 보내고, receive를 이용하여 값을 받는다.
Queue와 다른 점은 더이상 다른 원소들이 오지 않는 다는 것을 알리기 위해 닫힐 수 있다.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x * x)
channel.close()
}
for (y in channel) println(y)
println("Done!")
}
1
4
9
16
25
Done!
와 같이 결과가 나온다.
close() 함수를 통해 닫기 토큰을 받기 전에 보내진 모든 원소들이 수신되었음을 보장할 수 있다.