如何停止或取消Kotlin协程(立即停止当前运行的代码块)?

kx1ctssn  于 12个月前  发布在  Kotlin
关注(0)|答案(5)|浏览(204)

我想实现什么?

我有一个任务来下载图像,但随着屏幕滚动,它将取消以前的下载,并开始下载新的。我希望当它取消coroutine下载以前的图像它立即停止,并释放bandwidth,使新的图像下载更快。

我尝试了什么

我已经尝试了多种方法来停止coroutine,但它继续下去,直到它完成下载后,即使取消coroutine。当我取消coroutine,它使一个变量isActivefalse,并停止调用进一步暂停功能。但问题是,如果它运行loop1000000倍或下载图像从network这个任务不会被取消,除非完成。Like循环将完成它的1000000迭代,然后协程将被取消。
我试过这些,但没有成功:

job.cancel()
scope.cancel()

字符串
我已经尝试了很多方法来实现这一点,但没有得到解决方案。我不能使用任何库,现在在我的项目。

  • 这个用例不是由线程,执行器服务,协程实现的。因为所有的行为都是一样的。*

更多类似的问题:
How do I cancel a kotlin coroutine for a blocking download operation
AsyncTask is not cancelling the long running operation in android
Leaking Service held by Coroutine

pbpqsu0x

pbpqsu0x1#

一个Kotlin协程必须合作以允许取消。这意味着它有一些检查点调用一个suspend函数。这是有意义的,因为一些过程是原子的,不应该在中间停止。
一个不能取消的坏协程的例子:

var job = launch {
        var time = System.currentTimeMillis()
        var i = 0
        while (i < 1000) {
            if (System.currentTimeMillis() >= time) {
                println("Loop number ${++i} ")
                time += 500
            }
        }
    }

字符串
要使其可取消,您可以在每次迭代开始时添加yield()。以下是一个可取消的协程:

coroutineScope {
    var job = launch {
        var time = System.currentTimeMillis()
        var i = 0
        while (i<1000) {
            yield()
            if (System.currentTimeMillis() >= time) {
                println("Loop number ${++i}")
                time += 500
            }
        }
    }
    // wait some time
    delay(1300)
    println("Stopping the coroutine....")
    job.cancel()
    job.join()
    // or call job.cancelAndJoin()
}

xn1cxnb4

xn1cxnb42#

协程取消是合作的。协程代码必须合作才能取消。kotlinx.coroutines中的所有挂起函数都是可取消的。它们检查协程的取消并在取消时抛出CancellationException。但是,如果协程正在计算中工作并且不检查取消,则无法取消,如以下示例所示:

val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
    var nextPrintTime = startTime
    var i = 0
    while (i < 5) { // computation loop, just wastes CPU
        // print a message twice a second
        if (System.currentTimeMillis() >= nextPrintTime) {
            println("job: I'm sleeping ${i++} ...")
            nextPrintTime += 500L
        }
    }
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")

字符串
运行它,看看它是否会继续打印“I'm sleeping”,即使在取消之后,直到作业在五次迭代之后自行完成

使计算代码可取消

如下面的例子所示:

val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
    var nextPrintTime = startTime
    var i = 0
    while (isActive) { // cancellable computation loop
        // print a message twice a second
        if (System.currentTimeMillis() >= nextPrintTime) {
            println("job: I'm sleeping ${i++} ...")
            nextPrintTime += 500L
        }
    }
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")


参考这里的官方文档

jdgnovmf

jdgnovmf3#

尝试取消上下文:

this.coroutineContext.cancel()

字符串

7vux5j2d

7vux5j2d4#

我试着调用job.cancel(),但它不工作。但是当我调用job.cancel(null)时。它取消了当前正在运行的作业。你可以在cancel(cause:CancellationException? = null)的参数中传递nullCancellationException
我不知道为什么会发生这种情况的真实的原因,但它适用于job.cancel(null)。尽管如此,在这两种情况下,我们调用的是同一个函数。

ct2axkht

ct2axkht5#

协程不能在执行之间取消,你必须在你的代码之间添加isActive检查。

val job = CoroutineScope(Dispatchers.Default).launch {
    try {
        while (isActive) {
            // Your coroutine code here
            // Check for cancellation and exit the loop if needed
        }
    } finally {
        // Cleanup or finalization code
    }
}

字符串

相关问题