在协程中,当我要防止代码块被取消时,我应该在上下文中添加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)
}
答案 0 :(得分:2)
NonCancellable
不响应取消,而Job()
响应。
NonCancellable
以自定义方式实现Job
,并且其行为与使用可取消实现的Job()
不同。
cancel()
上的 NonCancellable
是空操作,不像Job()
那样,它会取消所有子协程,并且子协程中的任何崩溃都会传播到该父级{{1 }}。