myabcc17
myabcc17

Reputation: 93

Why can't kotlin coroutine be canceled?

I expected result of (0, 1) or (0, 1, 2), but print increasing number without stopping. I am studying about coroutine. I was following the example, but I was curious about it, so I wrote the code and found something strange.

class test {
    @Test
    fun test() = runBlocking {
        val startTime = System.currentTimeMillis()
        val job = launch {
            var nextTime = startTime
            var i = 0

            while (isActive) {
                if (System.currentTimeMillis() >= nextTime) {
                    println("${i++}")
                    nextTime += 500L
                }
            }
        }

        delay(1000L)
        println("[main]: tired for wait")
        job.cancelAndJoin()
        println("[main]: finish")
    }
}

---
0
1
2
3
4
5
6
...

But the code below worked as expected.

class test {
    @Test
    fun test() = runBlocking {
        val startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var nextTime = startTime
            var i = 0

            while (isActive) {
                if (System.currentTimeMillis() >= nextTime) {
                    println("${i++}")
                    nextTime += 500L
                }
            }
        }

        delay(1000L)
        println("[main]: tired for wait")
        job.cancelAndJoin()
        println("[main]: finish")
    }
}

----
0
1
2
[main]: tired for wait
[main]: finish

I just add Dispatchers.Default to launch. Why isn't the first code output as expected?

Upvotes: 0

Views: 338

Answers (1)

broot
broot

Reputation: 28452

This is because by default runBlocking() creates a single-threaded dispatcher for dispatching coroutines. Outer coroutine gets to delay() point, it suspends, then inner coroutine starts executing, but it never frees its thread, so outer coroutine can't resume. And because outer coroutine never resumes, it can't cancel the inner coroutine.

To avoid such problems, if you need to perform very long calculations, it is a good idea to call yield() from time to time. It lets free the thread for use by other coroutines.

Upvotes: 2

Related Questions