协程取消的一些思考

844 阅读2分钟

前言

写这个文章,应该是在有感而发。学习协程也算有些时候了。说很深入了解。那也是扯淡。废话不多说。开始吧

写一个协程运行一下

面试题目来了。请问下面的代码是如何执行的。

lifecycleScope.launch {
  var i = 0
  while (i < 10) {
      i++
      println("index $i isActive $isActive" )
      delay(100)
      if (i == 3) {
          cancel()
      }
  }
}

简单。运行效果如下。

index 1 isActive true
index 2 isActive true
index 3 isActive true
index 4 isActive false

再来一到题目,请问下面的代码执行顺序如何。

注意看 delay(100) 变成了Thread.sleep(100)

lifecycleScope.launch {
  var i = 0
  while (i < 10) {
      i++
      println("index $i isActive $isActive" )
      Thread.sleep(100)
      if (i == 3) {
          cancel()
      }
  }
}

这里也放出运行结果

I: index 1 isActive true
I: index 2 isActive true
I: index 3 isActive true
I: index 4 isActive false
I: index 5 isActive false
I: index 6 isActive false
I: index 7 isActive false
I: index 8 isActive false
I: index 9 isActive false
I: index 10 isActive false

是不是有些许玩味儿。分析一下delay(100)能够知道,它其实是一个挂起函数。

Thread.sleep(100)则是一个普通函数

cancel到底是干嘛的

为什么都是cancel().区别对待那么大呢?我们就需要搞懂cancel()到底是干嘛的。

在大佬秉心说TM的【译】如何优雅的处理协程的取消?。以及微信上在线问他,得到一个很重要的知识点 协程的取消并不是真正意义的取消

协程内部在cancel().的时候会记录状态。并且在下一个挂起点的时候才会取消。在第一个例子中,i == 3 的时候才cancel()打印中能看到index 4 isActive false 说明cancel()之后。协程内部依然在执行。只不过是状态发生了变化。

实际使用应该如何

在协程中可以使用withContext(Dispatchers.IO)去切换线程。这其实也是一个挂起函数。

所以啊。如果使用了挂起函数,那么效果就和delay(100)一样。而像第二个例子中。则可以使用 isActive做判断

lifecycleScope.launch {
  var i = 0
  while (i < 10) {
      if(!isActive){
        break
      }
      i++
      println("index $i isActive $isActive" )
      Thread.sleep(100)
      if (i == 3) {
          cancel()
      }
  }
}