1. suspend 함수란
suspend 함수는 코루틴 내에서만 호출할 수 있는 특수한 함수이다.
이 함수는 실행 중 특정 시점에서 중단되었다가 필요할 때 다시 실행을 재개할 수 있는 특성을 가진다.
2. 특징
- 중단 가능: suspend 함수는 특정 조건에서 중단될 수 있으며 이후 다시 재개된다.
- 코루틴 컨텍스트: suspend 함수는 호출될 때 코루틴의 컨텍스트에서 실행된다.
- 스레드가 항상 돌아오는 것은 아님: 중단된 suspend 함수가 다시 실행될 때 같은 스레드가 아니라 다른 스레드에서 실행될 수 있다. 코루틴 디스패처의 정책에 따라 결정된다.
- 직관적인 비동기 코드 작성: 콜백 지옥을 피하고, 비동기 코드를 동기 코드처럼 작성할 수 있다.
suspend fun example(st: String) {
println("작업 시작 $st - ${Thread.currentThread().name}")
delay(1000) // 1초 동안 중단
println("작업 재개 $st - ${Thread.currentThread().name}")
}
fun main(): Unit = runBlocking {
launch(Dispatchers.IO) {
example("qwer1") // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer2") // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer3") // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer4") // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer5") // 코루틴 내부에서 호출
}
}
작업이 재개 되었을 때 같은 스레드에서 시작되지 않을 수 도 있다.
suspend fun example(st: String, time: Long) {
delay(time) // 1초 동안 중단
println("작업 시작 $st - ${Thread.currentThread().name}")
delay(1000) // 1초 동안 중단
println("작업 재개 $st - ${Thread.currentThread().name}")
}
fun main(): Unit = runBlocking {
launch(Dispatchers.IO) {
example("qwer1", 500) // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer2", 0) // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer3", 100) // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer4", 200) // 코루틴 내부에서 호출
}
launch(Dispatchers.IO) {
example("qwer5", 300) // 코루틴 내부에서 호출
}
}
suspend함수는 스레드를 블로킹하지 않기 때문에 다른 작업에서 스레드를 활용할 수 있다.
3. 동작 원리
- Continuation 인터페이스: suspend 함수는 컴파일 시 Continuation<T>라는 인터페이스를 이용해 상태를 관리하도록 변환된다. 함수의 호출 상태를 저장하고 재개 시 이전 상태를 복원한다.
- 스택리스(stackless) 설계: 코루틴은 스택을 사용하지 않는 구조로 설계되어 있다. 쉽게 말해 suspend 함수가 내부적으로 다른 suspend 함수를 호출하더라도 호출 스택이 점점 깊어지는 방식으로 동작하지 않는대신 호출된 각 함수는 상태 머신으로 변환되어 호출 상태를 저장하고 필요할 때 재개될 수 있도록 관리한다.
suspend fun functionA() {
println("A 시작")
functionB()
println("A 종료")
}
suspend fun functionB() {
println("B 시작")
delay(1000) // 중단 지점
println("B 종료")
}
fun main() = runBlocking {
functionA()
}
//변환 코드
class FunctionAContinuation : Continuation<Unit> {
var state = 0
override fun resumeWith(result: Result<Unit>) {
when (state) {
0 -> {
println("A 시작")
state = 1
// 호출스택으로 쌓이지 않고 기존호출이 제거되고 상태를 저장한다.
FunctionBContinuation().resumeWith(Result.success(Unit))
}
1 -> {
println("A 종료")
}
}
}
}
class FunctionBContinuation : Continuation<Unit> {
var state = 0
override fun resumeWith(result: Result<Unit>) {
when (state) {
0 -> {
println("B 시작")
state = 1
delay(1000, this) // 중단
}
1 -> {
println("B 종료")
}
}
}
}
참고
https://www.youtube.com/watch?v=IQf-vtIC-Uc&ab_channel=AndroidDevelopers
'개념 저장소 > coroutine' 카테고리의 다른 글
Coroutine의 실행 옵션 (2) | 2025.01.01 |
---|---|
공유 자원 관리 (1) | 2025.01.01 |
코루틴에서의 예외 처리 (0) | 2024.12.22 |
구조화된 동시성 (2) | 2024.12.21 |
Coroutine Context (1) | 2024.12.12 |