You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin协程:join()与cancelAndJoin()的区别及测试疑问

Understanding cancelAndJoin() vs join() in Kotlin Coroutines

Great question! Let's unpack why you're seeing identical logs, and clarify the differences between these two methods step by step.

First: Why Your Test Showed No Difference

The core issue here is that your coroutine's loop isn't cooperating with cancellation. Kotlin coroutines use cooperative cancellation—meaning a coroutine won't stop unless it explicitly checks for cancellation signals.

In your code, the while (i < 5) loop runs purely on CPU without any checks for whether the coroutine has been canceled. When you call cancel() (either directly or via cancelAndJoin()), the coroutine ignores the signal and keeps running until it finishes the loop naturally. That's why replacing cancelAndJoin() with just join() gives the same result: the coroutine wasn't actually being canceled in either case.

Breaking Down Each Method

Let's clarify what each function does:

  • join(): This suspends the current coroutine until the target job completes. It doesn't attempt to cancel the job—it just waits for it to finish on its own, whether that's by running to completion or being canceled by some other external trigger.

  • cancel(): This sends a cancellation signal to the target job, but it doesn't wait for the job to stop. The calling coroutine will immediately continue executing code after cancel(). However, the target job will only stop if it's set up to cooperate (e.g., checking isActive, calling a cancellable suspend function like delay(), or using yield()).

  • cancelAndJoin(): This is a convenience function that combines cancel() and join() into a single atomic call. It first sends the cancellation signal to the job, then suspends the current coroutine until the job fully completes (either by stopping due to cancellation or finishing its remaining cooperative work).

Fixing Your Example to See the Real Difference

To observe the distinct behavior, modify your loop to check the coroutine's active state:

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime
        var i = 0
        // Check if the coroutine is still active before continuing
        while (isActive && i < 5) {
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I'm sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }
    delay(1300L)
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // Now this will properly cancel the job and wait
    println("main: Now I can quit.")
}

Logs with cancelAndJoin():

job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.

Logs with join():

job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
job: I'm sleeping 3 ...
job: I'm sleeping 4 ...
main: Now I can quit.

Now you'll see the clear contrast: cancelAndJoin() stops the coroutine right after the cancellation signal, while join() just waits for the job to finish all its iterations.

Quick Recap

  • Use join() when you need to wait for a job to finish without interrupting it.
  • Use cancel() when you want to send a cancellation signal but don't need to wait for the job to stop.
  • Use cancelAndJoin() when you want to both cancel the job and wait for it to complete its cancellation process.

内容的提问来源于stack exchange,提问作者AndDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 09:37:39