๐ก ์ฝ๋ฃจํด์ ์๋ ์๋ฆฌ ์ค ์์ฃผ ์ค์ํ ๊ธฐ๋ฅ์ธ ์ฝ๋ฃจํด ์์ธ ์ฒ๋ฆฌ์ ๋ํ์ฌ ํ์ตํ์์ต๋๋ค.
์์ธ ์ฒ๋ฆฌ
- ์กํ์ง ์๋ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ํ๋ก๊ทธ๋จ์ด ์ข ๋ฃ๋๋ ๊ฒ์ฒ๋ผ, ์ฝ๋ฃจํด๋ ์กํ์ง ์์ ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ์ข ๋ฃ๋ฉ๋๋ค.
- ํฐ ์ฐจ์ด๋ ์ฝ๋ฃจํด ๋น๋๋ ๋ถ๋ชจ๋ ์ข ๋ฃ์ํค๋ฉฐ, ์ทจ์๋ ๋ถ๋ชจ๋ ์์๋ค ๋ชจ๋๋ฅผ ์ทจ์์ํต๋๋ค.
fun main(): Unit = runBlocking {
launch {
launch {
delay(1000)
throw Error("Error")
}
launch {
delay(2000)
println("Will not be printed")
}
launch {
delay(500)
println("Will be printed")
}
}
launch {
delay(2000)
println("Will not be printed")
}
}
// Will be printed
// Exception in thread "main" java.lang.Error: Some error...
- ์์ธ๋ ์์์์ ๋ถ๋ชจ๋ก ์ ํ๋๋ฉฐ, ๋ถ๋ชจ๊ฐ ์ทจ์๋๋ฉด ์์๋ ์ทจ์๋๊ธฐ ๋๋ฌธ์ ์๋ฐฉ์ผ๋ก ์ ํ๋ฉ๋๋ค.
- ์์ธ ์ ํ๊ฐ ์ ์ง๋์ง ์์ผ๋ฉด, ๊ณํต ๊ตฌ์กฐ์ ๋ชจ๋ ์ฝ๋ฃจํด์ด ์ทจ์๋๊ฒ ๋ฉ๋๋ค.
์ฝ๋ฃจํด ์ข ๋ฃ ๋ฉ์ถ๊ธฐ
- ์ฝ๋ฃจํด ๊ฐ์ ์ํธ์์ฉ์ ์ก์ ํตํด์ ์ผ์ด๋๊ธฐ ๋๋ฌธ์, ์ฝ๋ฃจํด ๋น๋ ๋ด๋ถ์์ ์๋ก์ด ์ฝ๋ฃจํด ๋น๋๋ฅผ try-catch ๋ฌธ์ ํตํด ๋ํํ๋ ๊ฑด ์ ํ ๋์์ด ๋์ง ๋ชปํฉ๋๋ค.
- ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ์์ธ ์ ํ๋ฅผ ๋ฉ์ถ๋ ๋ฐฉ๋ฒ์ ๋ํ์ฌ ์์๋ณด๊ฒ ์ต๋๋ค.
SupervisorJob
- supervisorJob์ ์ฌ์ฉํ๋ฉด, ์์์์ ๋ฐ์ํ ๋ชจ๋ ์์ธ๋ฅผ ๋ฌด์ํ ์ ์์ต๋๋ค.
- ๋ถ๋ชจ ํน์ ๋ค๋ฅธ ์์๋ค์๊ฒ ์์ ์ ์์ธ๊ฐ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค.
val scope = CoroutineScope(SupervisorJob())
scope.launch {
// child1
}
scope.launch {
// child2
}
- ์ ์ฝ๋์์ child1์์ ์์ธ๊ฐ ๋ฐ์ํด๋ child2๋ก ์์ธ๊ฐ ์ ํ๋์ง ์์ต๋๋ค.
- SupervisorJob์ ์ฌ์ฉํ ๋ ๊ฐ์ฅ ํํ ์ค์๋ ๋ถ๋ชจ ์ฝ๋ฃจํด์ด ์์ด SupervisorJob์ ํ์ฉํ๋ ๊ฒ์ ๋๋ค.
fun main(): Unit = runBlocking {
// ๋ถ๋ชจ ์ฝ๋ฃจํด์ด ์๋ ์ก์ ์ผ๋ฐ ์ก๊ณผ ๋์ผํ๊ฒ ์๋
launch(SupervisorJob()) {
launch {
throw Error("error")
}
}
}
// Exception
- ํ๋์ ์ฝ๋ฃจํด์ด ์ทจ์๋์ด๋ ๋ค๋ฅธ ์ฝ๋ฃจํด์ด ์ทจ์๋์ง ์๋๋ค๋ ํน์ง์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์ก์ ๋ค์์ ์ฝ๋ฃจํด์์ ์ปจํ ์คํธ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์ข ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
val job = SupervisorJob()
launch(job) {
throw Error("Error1")
}
launch(job) {
throw Error("Error2")
}
supervisorScope
- ์ฝ๋ฃจํด ๋น๋๋ฅผ supervisorScope๋ก ๋ํํ๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค.
- ์ด ๋ฐฉ์์ ๋ค๋ฅธ ์ฝ๋ฃจํด์์ ๋ฐ์ํ ์์ธ๋ฅผ ๋ฌด์ํ๊ณ ๋ถ๋ชจ์์ ์ฐ๊ฒฐ์ ์ ์งํฉ๋๋ค.
// in runBlocking
supervisorScope {
launch {
throw Error("error")
}
launch {
throw Error("error")
}
}
- ์ด๋ ๋จ์ง ์ค๋จ ํจ์์ผ ๋ฟ์ด๋ฉฐ, ์ค๋จ ํจ์ ๋ณธ์ฒด๋ฅผ ๋ํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก ์๋ก ๋ฌด๊ดํ ๋ค์์ ์์ ์ ์ค์ฝํ ๋ด์์ ์คํํ ๋ ํ์ฉํ ์ ์์ต๋๋ค.
suspend fun notifyAnalytics(actions: List<UserAction>) =
supervisorScope {
actions.forEach { action ->
launch {
notifyAnalytics(action)
}
}
}
- ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ supervisorScope๋ withContext(SupervisorJob())์ผ๋ก ๋์ฒด๋ ์ ์์ต๋๋ค.
// ์๋ชป ๋ ์ฝ๋์
๋๋ค !
suspend fun notifyAnalytics(actions: List<UserAction>) =
withContext(SupervisorJob()) {}
- ์ ์ฝ๋๋ Job์ด ์์๋์ง ์๋ ์ ์ผํ ์ปจํ ์คํธ์ด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
- ์ฝ๋ฃจํด์ ๊ฐ์ ์์ ๋ง์ ์ก์ ๊ฐ์ง๊ณ ์๊ณ , ์ก์ ๋ค๋ฅธ ์ฝ๋ฃจํด์ ์ ๋ฌํ์ฌ ๋ถ๋ชจ ๊ด๊ณ๋ฅผ ๋งบ์ต๋๋ค.
- ์ ์ฝ๋์์๋ ์์ธ ๋ฐ์ ์ withContext ์ฝ๋ฃจํด์ผ๋ก ์ ๋ฌ๋๋ฉฐ, ๋ถ๋ชจ์ ์์ ์ฝ๋ฃจํด์ด ๋ชจ๋ ์ทจ์๋์ด ๋ง์ง๋ง์ผ๋ก ์์ธ๊ฐ ๋์ ธ์ง๋๋ค.
- ์ด๋ SupervisorJob์ ๋ถ๋ชจ๋ก ๋ฐ๊พธ์ด๋ ์ํ๋ ํจ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
await
- ์์ธ๊ฐ ๋ฐ์ํ์ ๋ async ์ฝ๋ฃจํด ๋น๋๋ launch์ฒ๋ผ ๋ถ๋ชจ ์ฝ๋ฃจํด์ ์ข ๋ฃํ๊ณ ๋ถ๋ชจ์ ๊ด๋ จ์๋ ๋ค๋ฅธ ์ฝ๋ฃจํด ๋น๋๋ ์ข ๋ฃ์ํต๋๋ค.
suspend fun main() = supervisorScope {
val str1 = async<String> {
throw Exception()
}
val str2 = async {
"Text2"
}
try {
println(str1.await())
} catch( e: Exception) {
println(e)
}
println(str2.await())
}
// Exception
// Text2
- ์ฝ๋ฃจํด์ด ์์ธ๋ก ์ข ๋ฃ๋์๊ธฐ ๋๋ฌธ์ ๋ฐํํ ๊ฐ์ด ์์ง๋ง, await๊ฐ Exception์ ๋์ ธ์ Exception์ด ์ถ๋ ฅ๋ฉ๋๋ค.
- supervisorScope๊ฐ ์ฌ์ฉ๋์๊ธฐ ๋๋ฌธ์ ๋ ๋ค๋ฅธ async๋ ์ค๋จ๋์ง ์๊ณ ๋๊น์ง ์คํ๋ฉ๋๋ค.
CancellationException
- ์์ธ๊ฐ CancellationException์ ์๋ธํด๋์ค๋ผ๋ฉด ๋ถ๋ชจ๋ก ์ ํ๋์ง ์๊ณ , ํ์ฌ ์ฝ๋ฃจํด ์ทจ์๋ง ์งํํฉ๋๋ค.
- CancellationException์ ์ด๋ฆฐ ํด๋์ค์ด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํด๋์ค๋ ๊ฐ์ฒด๋ก ํ์ฅ๋ ์ ์์ต๋๋ค.
object MyException : CancellationException()
suspend fun main(): Unit = coroutineScope {
launch { // 1 coroutine
launch { // 2 coroutine
delay(2000)
println("not be printed")
}
throw MyException
}
launch { // 3 coroutine
delay(2000)
println("be printed")
}
}
// 2์ด ํ..
// be printed
- ๋ ๊ฐ์ ์ฝ๋ฃจํด์ด 1, 3 ๋น๋๋ก ์์๋ฉ๋๋ค.
- 1์์ CancellationException์ ์์ธ์ธ MyException๋ฅผ ๋์ง๋ฏ๋ก, ์์ธ๋ 1์์ ์์๋ launch์์ ์กํ๊ฒ ๋ฉ๋๋ค.
- 1์์ ์์๋ ์ฝ๋ฃจํด์ ์๊ธฐ ์์ ๊ณผ ์์์ธ 2 ๋น๋๋ฅผ ์ทจ์ํ๊ณ , 3 ๋น๋๋ ์๋ฌด๋ฐ ์ํฅ์ ๋ฐ์ง ์๊ณ ์ ์์ ์ผ๋ก ์คํ๋ฉ๋๋ค.
์ฝ๋ฃจํด ์์ธ ํธ๋ค๋ฌ
- ์์ธ๋ฅผ ๋ค๋ฃฐ ๋ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ธฐ๋ณธ ํ๋์ ์ ์ํ๋ ๊ฒ์ด ์ ์ฉํฉ๋๋ค.
- ์ฝ๋ฃจํด์์๋ ์ด๋ฐ ๊ฒฝ์ฐ์ CoroutineExceptionHandler ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์์ธ ์ ํ๋ฅผ ์ค๋จ์ํค์ง ์๊ณ ์์ธ๊ฐ ๋ฐ์ํ ๋ ํด์ผํ ๊ฒ๋ค์ ์ ์ํ๋ ๋ฐ ์ฌ์ฉํฉ๋๋ค.
fun main(): Unit = runBlocking {
val handler =
CoroutineExceptionHandler { ctx, exception ->
println("Caught $exception")
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
delay(1000)
throw Error("Error")
}
delay(3000)
}
// Caught java.lang.Error: Error
์ฐธ๊ณ
https://tak8997.github.io/2021/04/17/Kotlin-Coroutine-Exception-Handling/