Kotlin协程:使用NonCancellable和独立的新Job之间有什么区别

时间:2020-10-16 12:57:30

标签: kotlin kotlin-coroutines

在协程中,当我要防止代码块被取消时,我应该在上下文中添加NonCancellable:

@Test
fun coroutineCancellation_NonCancellable() {
    runBlocking {
        val scopeJob = Job()
        val scope = CoroutineScope(scopeJob + Dispatchers.Default + CoroutineName("outer scope"))
        val launchJob = scope.launch(CoroutineName("cancelled coroutine")) {
            launch (CoroutineName("nested coroutine")) {
                withContext(NonCancellable) {
                    delay(1000)
                }
            }
        }
        scope.launch {
            delay(100)
            launchJob.cancel()
        }
        launchJob.join()
    }
}

即使长时间运行的协程在100毫秒后被取消,上述单元测试也需要约1.1秒才能执行。那是NonCancellable的效果,我明白这一点。

但是,下面的代码在功能上似乎是等效的:

@Test
fun coroutineCancellation_newJobInsteadOfNonCancellable() {
    runBlocking {
        val scopeJob = Job()
        val scope = CoroutineScope(scopeJob + Dispatchers.Default + CoroutineName("outer scope"))
        val launchJob = scope.launch(CoroutineName("cancelled coroutine")) {
            launch (CoroutineName("nested coroutine")) {
                withContext(Job()) {
                    delay(1000)
                }
            }
        }
        scope.launch {
            delay(100)
            launchJob.cancel()
        }
        launchJob.join()
    }
}

我试图在取消,错误处理和常规功能方面找到这两种方法之间的任何功能差异,但到目前为止,我还没有发现。目前,看起来NonCancellable在框架中只是为了提高可读性。

现在,可读性很重要,因此我宁愿在代码中使用NonCancellable。但是,its documentation听起来似乎与常规Job有所不同,所以我想详细了解这一方面。

所以,我的问题是:两种方法之间是否存在功能上的区别(即,如何修改这些单元测试以使结果有所不同?)?

编辑:

按照路易斯的回答,我测试了“使清理无法取消”的情况,在这种情况下,Job()也类似于NonCancellable。在以下示例中,即使协程刚在200毫秒后被取消,单元测试也将运行1秒钟以上:

@Test
fun coroutineCancellation_jobInsteadOfNonCancellableInCleanup() {
    runBlocking {
        val scope = CoroutineScope(Job() + Dispatchers.Default + CoroutineName("outer scope"))
        val launchJob = scope.launch(CoroutineName("test coroutine")) {
            try {
                delay(100)
                throw java.lang.RuntimeException()
            } catch (e: Exception) {
                withContext(Job()) {
                    cleanup()
                }
            }
        }
        scope.launch {
            delay(200)
            launchJob.cancel()
        }
        launchJob.join()
    }
}

private suspend fun cleanup() {
    delay(1000)
}

1 个答案:

答案 0 :(得分:2)

NonCancellable不响应取消,而Job()响应。

NonCancellable以自定义方式实现Job,并且其行为与使用可取消实现的Job()不同。

cancel()上的

NonCancellable是空操作,不像Job()那样,它会取消所有子协程,并且子协程中的任何崩溃都会传播到该父级{{1 }}。