π‘ μ€λ¨ ν¨μλ₯Ό μ¬μ©νλ μ½λ£¨ν΄μμ μ·¨μλ₯Ό ν΅ν΄μ μμ λλΉμ λ©λͺ¨λ¦¬ λμλ₯Ό μ€μΌ μ μλ λ°©λ²μ λνμ¬ νμ΅νμμ΅λλ€.
cancellation
- μ€λ¨ ν¨μλ₯Ό μ¬μ©νλ λͺλͺ ν΄λμ€μ λΌμ΄λΈλ¬λ¦¬λ νμ μ·¨μλ₯Ό μ§μν©λλ€.
- λ¨μν μ€λ λλ₯Ό μ£½μ΄λ©΄, μ°κ²°μ λ«κ³ μμμ ν΄μ νλ κΈ°νκ° μκΈ° λλ¬Έμ μ΅μ μ μ·¨μ λ°©λ²μ΄ λ©λλ€.
- κ°λ°μλ€μ΄ μνκ° μ‘ν°λΈνμ§ νμΈνμ§ μμλ λκ³ , κ°λ¨νκ³ μμ ν λ°©μμΌλ‘ μ·¨μν μ μλλ‘ μ½λ£¨ν΄μ μ΄λ₯Ό μ§μνκ³ μμ΅λλ€.
κΈ°λ³Έμ μΈ μ·¨μ
- Job μΈν°νμ΄μ€λ cancel λ©μλλ₯Ό ν΅ν΄μ μ·¨μλ₯Ό μ§μν©λλ€.
cancel()
- μ΄λ₯Ό νΈμΆν μ½λ£¨ν΄μ 첫 λ²μ§Έ μ€λ¨μ μμ μ‘μ λλ λλ€.
- μ‘μ΄ μμμ κ°μ§κ³ μλ°λ©΄ κ·Έλ€ λν μ·¨μλλ©°, λΆλͺ¨λ μν₯μ λ°μ§ μμ΅λλ€.
- μ‘μ΄ μ·¨μλλ©΄, μ·¨μλ μ‘μ μλ‘μ΄ μ½λ£¨ν΄μ λΆλͺ¨λ‘ μ¬μ©λ μ μμ΅λλ€.
- μ‘μ μ·¨μνλ©΄, Cancelling → Cancelled μνλ‘ λ³ν©λλ€.
val job = launch {
repeat(1_000) { i ->
delay(200)
println("$i")
}
}
delay(1100)
job.cancel()
job.join()
// 0
// 1
// 2
// 3
// 4
- job.join()μ λ€μ μΆκ°νλ©΄, μ½λ£¨ν΄μ΄ μ·¨μλ₯Ό λ§μΉ λκΉμ§ μ€λ¨λλ―λ‘ κ²½μ μνκ° λ°μνμ§ μμ΅λλ€.
cancelAndJoin()
- kotlin.coroutines λΌμ΄λΈλ¬λ¦¬λ cancelκ³Ό joinμ ν¨κ» νΈμΆν μ μλ νμ₯ν¨μλ₯Ό μ 곡ν©λλ€.
public suspend fun Job.cancelAndJoin() {
cancel()
return join()
}
- μ΄ λ°©λ²μΌλ‘ Jobκ³Ό μ°κ²° λ μλ§μ μ½λ£¨ν΄μ νλ²μ μ·¨μν μ μμ΅λλ€.
- νλ«νΌμ μ’ λ₯λ₯Ό λΆλ¬Ένκ³ λμμ μνλλ μμ κ·Έλ£Ή μ 체λ₯Ό μ·¨μμμΌμΌ ν λκ° μμ΅λλ€.
- μλλ‘μ΄λλ‘ μλ₯Ό λ€λ©΄ μ¬μ©μκ° λ·° μ°½μ λκ°μ λ, λ·°μμ μμλ λͺ¨λ μ½λ£¨ν΄μ μ·¨μνλ κ²½μ°μ λλ€.
μ·¨μμ μλ λ°©μ
- μ‘μ΄ μ·¨μλλ©΄ Cancelling μνλ‘ λ°λλλ€.
- μνκ° λ°λ λ€ μ²« λ²μ§Έ μ€λ¨μ μμ CancellationException μμΈλ₯Ό λμ§λ©°, μμΈλ₯Ό μ‘κ±°λ λ€μ λμ§ μ μμ΅λλ€.
// in coroutineScope
val job = Job()
launch(job){
try {
repeat(1_000) { i ->
delay(200)
println("$i")
}
} catch (e : CancellationException) {
println(e)
throw e
}
}
job.cancelAndJoin()
// JobCancellationException
μ·¨μ μ€ μ½λ£¨ν΄ ν λ² λ νΈμΆνκΈ°
- μ½λ£¨ν΄μ΄ μ€μ λ‘ μ’ λ£λκΈ° μ μ CancellationExceptionμ μ‘κ³ , μ’ λ λ§μ μ°μ°μ΄ μν κ°λ₯ν©λλ€.
- νμ§λ§ μ 리 κ³Όμ μ€μ μ€λ¨μ νμ©νμ§λ μμ΅λλ€.
- Jobμ΄ μ΄λ―Έ Cancelling μνκ° λμμ λ, μ€λ¨νκ±°λ λ€λ₯Έ μ½λ£¨ν΄μ μμνλ κ²μ λΆκ°λ₯ν©λλ€.
- μ΄λ° κ²½μ° withContextλ₯Ό μ¬μ©ν μ μμ΅λλ€.
- μ΄λ―Έ μ½λ£¨ν΄μ΄ μ·¨μλμμ λ μ€λ¨ ν¨μλ₯Ό λ°λμ νΈμΆν΄μΌ νλ κ²½μ°κ° λ°μ
- λ°μ΄ν°λ² μ΄μ€μ λ³κ²½ μ¬νμ λ‘€λ°±ν΄μΌ νλ κ²½μ°
- μ΄λ μ½λ λΈλ‘μ 컨ν μ€νΈλ₯Ό λ°κΎΈλ κ²μ΄λ©°, withContext λ΄λΆμμλ μ·¨μλ μ μλ JobμΈ NonCancellable κ°μ²΄λ₯Ό μ¬μ©ν μ μμ΅λλ€.
- μ΄ λ λΈλ‘ λ΄λΆμμ μ‘μ μ‘ν°λΈ μνλ₯Ό μ μ§νλ©°, μ€λ¨ ν¨μλ₯Ό μνλ λ§νΌ νΈμΆν μ μμ΅λλ€.
val job = Job()
launch(job) {
try {
delay(200)
println("Coroutine finished")
} finally {
println("Finally")
withContext(NonCancellable){
delay(1000L)
println("Cleanup Done")
}
}
}
delay(100)
job.cancelAndJoin()
println("Done")
// Finally
// Cleanup Done
// Done
invokeOnCompletion()
- μμμ ν΄μ νλ λ° μμ£Ό μ¬μ©λλ Jobμ λ©μλμ λλ€.
- μ‘μ΄ Completedλ Cancelledμ κ°μ λ§μ§λ§ μνμ λλ¬νμ λ νΈμΆλ νΈλ€λ¬λ₯Ό μ§μ νλ μν μ ν©λλ€.
val job = launch {
delay(1000)
}
job.invokeOnCompletion { exception: Throwable? ->
println("Finished")
}
delay(400)
job.cancelAndJoin()
// Finished
- νΈλ€λ¬μ νλΌλ―Έν° μ€ νλμΈ μμΈμ μ’
λ₯λ λ€μκ³Ό κ°μ΅λλ€.
- μ‘μ΄ μμΈ μμ΄ λλλ©΄ nullμ΄ λ©λλ€.
- μ½λ£¨ν΄μ΄ μ·¨μλμμΌλ©΄ CancellationExceptionμ΄ λ©λλ€.
- μ½λ£¨ν΄μ μ’ λ£μν¨ μμΈκ° λ°μν μ μμ΅λλ€.
- μ΄ ν¨μλ μ·¨μνλ μ€μ λκΈ°μ μΌλ‘ νΈμΆλλ©°, μ΄λ€ μ€λ λμμ μ€νν μ§ κ²°μ ν μλ μμ΅λλ€.
μ€λ¨λ μ μλ κ±Έ μ€λ¨νκΈ°
- μ·¨μλ μ€λ¨μ μμ μΌμ΄λκΈ° λλ¬Έμ μ€λ¨μ μ΄ μμΌλ©΄ μ·¨μλ₯Ό ν μ μμ΅λλ€.
- μ€λ¨μ μν΄μ Thread.sleepμ μ¬μ©ν μ μμ§λ§, μ΄λ μ’μ§ μμ λ°©λ²μ΄λ©° μ΄λ° μν©μ λ체νλ μ¬λ¬κ°μ§ λ°©λ²μ΄ μμ΅λλ€.
yield()λ₯Ό μ£ΌκΈ°μ μΌλ‘ νΈμΆ
- yieldλ μ½λ£¨ν΄μ μ€λ¨νκ³ μ¦μ μ¬μ€νν©λλ€.
- μ€λ¨μ μ΄ μκ²ΌκΈ° λλ¬Έμ μ·¨μλ₯Ό ν¬ν¨ν΄ μ€λ¨ μ€μ νμν λͺ¨λ μμ μ ν μ μλ κΈ°νκ° μ£Όμ΄μ§λλ€.
- μ€λ¨ κ°λ₯νμ§ μμΌλ©΄μ CPU μ§μ½μ μ΄κ±°λ μκ° μ§μ½μ μΈ μ°μ°λ€μ΄ μ€λ¨ ν¨μμ μλ€λ©΄, κ° μ°μ°λ€ μ¬μ΄μ yieldλ₯Ό μ¬μ©νλ κ²μ΄ μ’μ΅λλ€.
suspend fun cpuIntensiveOperations() =
withContext(Dispatchers.Default) {
cpuIntensiveOperation1()
yield()
cpuIntensiveOperation2()
yield()
cpuIntensiveOperation3()
}
μ‘μ μνλ₯Ό μΆμ
- μ½λ£¨ν΄ λΉλ λ΄λΆμμ thisλ λΉλμ μ€μ½νλ₯Ό μ°Έμ‘°νκ³ μμ΅λλ€.
- coroutineContext[Job] λλ coroutineContext.jobμ ν΅ν΄μ νμ¬ μνκ° λ¬΄μμΈμ§ νμΈν μ μμΌλ©°, μ΄λ₯Ό ν§ν΄ μ‘ν°λΈνμ§ νμΈν μ μμ΅λλ€.
// isActive
public val CortoineScpoe.isActive : Boolean
get() = coroutineContext[Job]?.isActive ?: true
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job){
do {
// μνκ° μ‘ν°λΈν λ
} while(isActivte)
}
}
- λλ ensureActive() ν¨μλ₯Ό νμ©ν΄μ Jobμ΄ μ‘ν°λΈν μνκ° μλλ©΄ CancellationExceptionμ λμ§ μ μμ΅λλ€.
suspend fun main(): Unit = coroutineScope {
val job = Job()
launch(job) {
Thread.sleep(200)
ensureActive()
}
delay(100)
job.cancelAndJoin()
}
yield()μ ensureActive()μ μ°¨μ΄
- λ ν¨μ λͺ¨λ λ€λ₯Έ μ½λ£¨ν΄μ΄ μ€νν μ μλ κΈ°νλ₯Ό μ€λ€λ μ μμ κ²°κ³Όλ λΉμ·νμ§λ§, λ§€μ° λ€λ₯Έ κΈ°λ₯μ νκ³ μμ΅λλ€.
- ensuereActive() ν¨μλ λ°λμ CoroutineScope(λλ CoroutineContextλ Job)μμ νΈμΆλμ΄μΌ ν©λλ€.
- μΌλ°μ μΌλ‘ μ’ λ κ°λ²Όμ΄ μμ μ΄κΈ° λλ¬Έμ μμ£Ό μ¬μ©νκ³ μμ΅λλ€.
- yield()λ μ νμ μΈ μ΅μμ μ€λ¨ ν¨μμ
λλ€.
- μ€μ½νκ° νμνμ§ μκΈ° λλ¬Έμ μΌλ°μ μΈ μ€λ¨ ν¨μμμλ μ¬μ©λ μ μμ§λ§, λμ€ν¨μ²λ₯Ό μ¬μ©ν λ μ€λ λκ° λ°λλ λ¬Έμ κ° μκΈΈ μ μμ΅λλ€.
- λλ¬Έμ CPU μ¬μ©λμ΄ ν¬κ±°λ μ€λ λλ₯Ό λΈλ‘νΉνλ μ€λ¨ ν¨μμμ μμ£Ό μ¬μ©λ©λλ€.
suspendCancellableCoroutine
- μ΄ ν¨μλ suspendCoroutineκ³Ό λΉμ·νμ§λ§, 컨ν°λ΄μμ΄μ κ°μ²΄λ₯Ό λͺ κ°μ§ λ©μλκ° μΆκ°λ CancellableContinuation<T>λ‘ λνν©λλ€.
- κ°μ₯ μ€μν λ©μλλ μ½λ£¨ν΄μ΄ μ·¨μλμμ λ νλμ μ μνλ λ° μ¬μ©νλ invokeOnCancellation λ©μλμ λλ€.
- μ΄ λ©μλλ λΌμ΄λΈλ¬λ¦¬μ μ€νμ μ·¨μνκ±°λ μμμ ν΄μ ν λ μ£Όλ‘ μ¬μ©ν©λλ€.
suspend fun someTask() = suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation {
// μ 리 μμ
μ μν
}
// λλ¨Έμ§ κ΅¬ν λΆλΆ
}
- λν CancellableContinuation<T>μμλ (isActive, isCompleted, isCancelled νλ‘νΌν°λ₯Ό μ¬μ©) μ‘μ μνλ₯Ό νμΈν μ μμΌλ©°, 컨ν°λ΄μμ΄μ μ μ·¨μν λ μ·¨μκ° λλ μμΈμ μΆκ°μ μΌλ‘ μ 곡ν μ μμ΅λλ€.
μμ½
- μ·¨μλ μ½λ£¨ν΄μ κ°λ ₯νκ³ μ€μν κΈ°λ₯μ΄λ©°, μ¬μ©νλ λ° κΉλ€λ‘μ΄ κ²½μ°κ° μμ΅λλ€.
- νμ§λ§ μ·¨μλ₯Ό μ μ νκ² μ¬μ©νλ©΄ μμ λλΉμ λ©λͺ¨λ¦¬ λμλ₯Ό μ€μΌ μ μμ΅λλ€.