안드로이드 개발자 노트
[코루틴의 정석] 9장. 일시 중단 함수 본문
9.1. 일시 중단 함수와 코루틴
9.1.1. 일시 중단 함수란 무엇인가?
일시 중단 함수는 suspend fun 키워드로 선언되는 함수로 주로 코루틴의 비동기 작업과 관련된 코드들을 구조화 하고 재사용할 수 있는 코드의 집합으로 만드는데 사용된다.
fun main() = runBlocking<Unit> {
delayAndPrintHelloWorld()
delayAndPrintHelloWorld()
}
suspend fun delayAndPrintHelloWorld() {
delay(1000L)
println("Hello World")
}
9.1.2. 일시 중단 함수는 코루틴이 아니다
일시 중단 함수는 코루틴 내부에서 실행되는 코드의 집합일 뿐, 코루틴이 아니다.
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
delayAndPrintHelloWorld()
delayAndPrintHelloWorld()
println(getElapsedTime(startTime))
}
suspend fun delayAndPrintHelloWorld() {
delay(1000L)
println("Hello World")
}
/*
// 결과:
Hello World
Hello World
지난 시간: 2017ms
*/
9.1.3. 일시 중단 함수를 별도의 코루틴상에서 실행하기
만약 일시 중단 함수를 코루틴처럼 사용하고 싶다면 일시 중단 함수를 코루틴 빌더로 감싸면 된다.
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis()
launch {
delayAndPrintHelloWorld()
}
launch {
delayAndPrintHelloWorld()
}
println(getElapsedTime(startTime))
}
suspend fun delayAndPrintHelloWorld() {
delay(1000L)
println("Hello World")
}
/*
// 결과:
지난 시간: 3ms
Hello World
Hello World
*/
1초 정도가 지나고 나서 Hello World가 두 번 출력된다.
9.2. 일시 중단 함수의 사용
9.2.1. 일시 중단 함수의 호출 가능 지점
일시 중단 함수는 일시 중단을 할 수 있는 곳에서만 호출할 수 있으며, 일시 중단이 가능한 지점은 다음 두 가지이다.
- 코루틴 내부
- 일시 중단 함수
9.2.1.1. 코루틴 내부에서 일시 중단 함수 호출하기
코루틴은 언제든지 일시 중단 함수를 호출할 수 있다.
fun main() = runBlocking<Unit> {
delayAndPrint(keyword = "I'm Parent Coroutine")
launch {
delayAndPrint(keyword = "I'm Child Coroutine")
}
}
suspend fun delayAndPrint(keyword: String) {
delay(1000L)
println(keyword)
}
/*
// 결과:
I'm Parent Coroutine
I'm Child Coroutine
*/
9.2.1.2. 일시 중단 함수에서 다른 일시 중단 함수 호출하기
일시 중단 함수 내부에서 일시 중단 함수를 호출할 수 있다.
suspend fun searchByKeyword(keyword: String): Array<String> {
val dbResults = searchFromDB(keyword)
val serverResults = searchFromServer(keyword)
return arrayOf(*dbResults, *serverResults)
}
suspend fun searchFromDB(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[DB]${keyword}1", "[DB]${keyword}2")
}
suspend fun searchFromServer(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[Server]${keyword}1", "[Server]${keyword}2")
}
9.2.2. 일시 중단 함수에서 코루틴 실행하기
9.2.2.1. 일시 중단 함수에서 코루틴 빌더 호출 시 생기는 문제
코루틴 빌더 함수는 CoroutineScope의 확장 함수로 선언돼 있기 때문에 일시 중단 함수에서 사용할 수 없다.
9.2.2.2. coroutineScope 사용해 일시 중단 함수에서 코루틴 실행하기
일시 중단 함수에서 코루틴 빌더 함수를 사용하려면, coroutineScope를 사용하면 된다.
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R
coroutineScope 일시 중단 함수를 사용하면 일시 중단 함수 내부에 새로운 CoroutineScope 객체를 생성할 수 있다.
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis() // 1. 시작 시간 기록
val results = searchByKeyword("Keyword") // 2. 검색 실행 및 결과 값 반환 받기
println("[결과] ${results.toList()}") // 3. 결과값 출력
println(getElapsedTime(startTime)) // 4. 지난 시간 표시
}
suspend fun searchByKeyword(keyword: String): Array<String> = coroutineScope { // this: CoroutineScope
val dbResultsDeferred = async {
searchFromDB(keyword)
}
val serverResultsDeferred = async {
searchFromServer(keyword)
}
return@coroutineScope arrayOf(*dbResultsDeferred.await(), *serverResultsDeferred.await())
}
suspend fun searchFromDB(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[DB]${keyword}1", "[DB]${keyword}2")
}
suspend fun searchFromServer(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[Server]${keyword}1", "[Server]${keyword}2")
}
fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms"
/*
// 결과:
[결과] [[DB]Keyword1, [DB]Keyword2, [Server]Keyword1, [Server]Keyword2]
지난 시간: 1039ms
*/
9.2.2.3. supervisorScope 사용해 일시 중단 함수에서 코루틴 실행하기
supervisorScope 일시 중단 함수는 Job 대신 SupervisorJob 객체를 생성한다는 점을 제외하고는 coroutineScope 일시 중단 함수와 같이 동작한다.
fun main() = runBlocking<Unit> {
println("[결과] ${searchByKeyword("Keyword").toList()}")
}
/*
// 결과:
[결과] [[Server]Keyword1, [Server]Keyword2]
*/
suspend fun searchByKeyword(keyword: String): Array<String> = supervisorScope { // this: CoroutineScope
val dbResultsDeferred = async {
throw Exception("dbResultsDeferred에서 예외가 발생했습니다")
searchFromDB(keyword)
}
val serverResultsDeferred = async {
searchFromServer(keyword)
}
val dbResults = try {
dbResultsDeferred.await()
} catch (e: Exception) {
arrayOf() // 예외 발생 시 빈 결과 반환
}
val serverResults = try {
serverResultsDeferred.await()
} catch (e: Exception) {
arrayOf() // 에러 발생 시 빈 결과 반환
}
return@supervisorScope arrayOf(*dbResults, *serverResults)
}
suspend fun searchFromDB(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[DB]${keyword}1", "[DB]${keyword}2")
}
suspend fun searchFromServer(keyword: String): Array<String> {
delay(1000L)
return arrayOf("[Server]${keyword}1", "[Server]${keyword}2")
}
요약
- 일시 중단 함수는 suspend fun 키워드로 선언되며, 일시 중단 지점이 포힘된 코드를 재사용이 가능한 단위로 만들어 구조화하는 데 사용된다.
- 일시 중단 함수는 코루틴이 아니며, 일시 중단 지점을 포함할 수 있는 코드의 집합일 뿐이다.
- 일시 중단 함수가 일반 함수와 다른 점은 일시 중단 지점을 포함한다는 것이다.
- 일시 중단 함수는 코루틴이나 다른 일시 중단 함수 내부 등 일시 중단이 가능한 지점에서만 호출될 수 있다.
- 일시 중단 함수 내부에서 coroutineScope 함수를 사용해 코루틴의 구조화를 깨지 않는 새로운 CoroutineScope 객체를 생성할 수 있다.
- coroutineScope 함수를 사용해 만든 CoroutineScope 객체를 사용해 launch나 async 같은 코루틴 빌더를 호출할 수 있다. 이를 사용하면 일시 중 단 함수 내부에서 비동기 작업을 병렬로 실행할 수 있다.
- coroutineScope 함수 대신 supervisorScope 함수를 사용해 일시 중단 함수 내부에서 생성된 코루틴의 예외 전파를 제한할 수 있다.
'Kotlin > 코루틴의 정석' 카테고리의 다른 글
[코루틴의 정석] 10장. 코루틴의 이해 (1) | 2024.09.28 |
---|---|
[코루틴의 정석] 8장. 예외처리 (0) | 2024.09.18 |
[코루틴의 정석] 7장. 구조화된 동시성 (0) | 2024.09.08 |