코루틴은 코틀린 뿐 아니라 여러 프로그래밍 언어에서 쓰이는 개념입니다.
Blocking은 어떤 함수가 실행 중일 때 해당 함수가 반환하기 전까지는 다른 작업을 수행할 수 없는 상태를 말합니다
Non-blocking은 어떤 함수가 실행 중일 때에도 다른 작업을 수행할 수 있는 상태를 말합니다.
코루틴에서 blocking 함수를 호출할 경우, 해당 함수의 실행이 완료될 때까지 다른 작업을 수행할 수 없으므로, 코루틴의 실행이 일시 중지되며, 블로킹 상태에 빠지게 됩니다. 반면, non-blocking 함수를 호출할 경우, 해당 함수의 실행이 완료되지 않아도 다른 작업을 수행할 수 있으므로, 코루틴의 실행이 계속됩니다. 이러한 방식으로, 코루틴에서 비동기적으로 여러 작업을 처리할 수 있게 됩니다.
dependencies {
.....
.....
//coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}
fun main() = runBlocking {
GlobalScope.launch {
delay(1000L)
println("World!")
}
println("Hello,")
delay(500L)
}
위 코드는 runBlocking 내부에 GlobalScope.launch으로 다른 코루틴을 실행한 코드입니다.
runBlocking{ }으로 현재 스레드를 점유하도록 하여 메인스레드가 코루틴의 작업이 다 끝날때 까지 기다리도록 했습니다.
그러나 GlobalScope.launch를 통해 코루틴을 실행하면 종료를 대기하는 특성이 사라집니다.
GlobalScope는 전역 범위에서 코루틴을 실행하는 범용 범위입니다.(Android Application과 라이프사이클리 동일한 코루틴) 이는 앱의 수명 주기에 종속되지 않으며 독립적으로 실행됩니다. 따라서 코루틴이 완료되지 않더라도 앱이 종료되어도 계속 실행될 수 있습니다.
따라서 위 코드에서 delay(500L) 이후에도 코루틴이 완료될 때까지 프로그램이 종료되지 않습니다.
[결과]
Hello,
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
위 코드는 runBlocking{ } 내부에 launch로 코루틴을 실행했습니다.
launch는 현재 runBlocking 블록과 같은 범위에서 코루틴을 실행하는 범위입니다. 이는 앱의 수명 주기에 종속됩니다.
따라서 코루틴의 작업이 모두 끝나기 전 까지 메인스레드가 종료되지 않습니다.
[결과]
Hello,
World!
suspend 함수는 일시 중단되고 다른 작업으로 제어 흐름이 이동할 수 있는 지점이 됩니다. 일반적으로 비동기적인 작업을 수행하는 동안 suspend 함수가 일시 중단됩니다. 이 함수는 완료되기를 기다리는 동안 다른 코루틴이 실행될 수 있도록 허용합니다.
즉, suspend 함수는 코루틴에서 비동기 작업을 간편하게 처리하고 효과적인 동시성을 구현하는 데 유용한 기능입니다.
하위 메서드로 refactoring하여 다른 메서드를 호출할 수 있습니다.
// runBlocking : 현재 코루틴 종료대기. 현재 Thread 점유.
fun main() = runBlocking { // this: CoroutineScope
launch { doWorld() }
println("Hello")
}
// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")
}
[결과]
Hello
World!
안드로이드에서 네트워크 요청을 수행하는 함수는 일반적으로 suspend 함수로 구현됩니다.
이 함수는 네트워크 요청을 보낸 후 결과를 기다리기 위해 일시 중단되며, 네트워크 작업이 완료되면 해당 지점에서 재개됩니다.
suspend fun fetchUserData(): UserData {
// 네트워크 요청 보내기
val response = apiService.getUserData()
// 응답 처리 및 결과 반환
return processResponse(response)
}
fun main() = runBlocking {
doWorld()
println("Done")
}
// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(2000L)
println("World 2")
}
launch {
delay(1000L)
println("World 1")
}
println("Hello")
}
[결과]
Hello
World 1
World 2
Done
launch와 async는 coroutineScope 내에서 사용되는 코루틴 빌더입니다. 그러나 두 빌더는 목적과 반환값에 차이가 있습니다.
launch는 가볍고 비동기적인 작업을 실행하기 위해 사용됩니다. launch를 사용하여 코루틴을 시작하면, 해당 코루틴은 완료 결과를 반환하지 않습니다. 따라서 launch는 실행 결과를 기다리지 않고 즉시 다음 코드로 진행합니다. 즉, launch는 "fire-and-forget" 방식으로 사용되며, 결과에 대한 관심이 없는 경우에 유용합니다.
coroutineScope {
launch {
// 비동기 작업 실행
}
// 다음 코드
}
async는 비동기 작업을 실행하고 해당 작업의 결과를 반환하는 데 사용됩니다. async는 Deferred 객체를 반환하며, 이 객체를 통해 비동기 작업이 완료되면 결과에 접근할 수 있습니다. async는 await() 함수를 사용하여 작업의 결과를 기다릴 수 있습니다. 따라서 async는 작업의 결과를 필요로 하는 경우에 유용합니다.
coroutineScope {
val deferredResult = async {
// 비동기 작업 실행
return "Result"
}
val result = deferredResult.await() // 작업의 결과를 기다림
// 결과를 사용한 다음 작업
}