为什么在Scala中当抛出异常时原子块会运行两次?

4

代码:

object Blub {
  import scala.concurrent.stm._
  val last = Ref("none")

  def bla() = atomic { implicit txn =>
    last() = "outer"
    try {
      atomic { implicit txn =>
        last() = "inner"
        println(last())
        throw new RuntimeException
      }
    } catch {
      case _: RuntimeException =>
    }
  }

  def main(args: Array[String]): Unit = {
    bla()
    println("Result: "+last.single())
  }
}

输出:

inner  
inner  
Result: outer

有人可以解释为什么内部原子块会运行两次吗?我知道由于异常它执行了回滚,因此得到了最终结果。但我不明白为什么要再次运行代码。

1个回答

4
在ScalaSTM文档的此页面底部有这样一段话:

为了使嵌套非常廉价,ScalaSTM尝试将所有嵌套级别合并到单个顶级事务中。如果内部事务抛出异常,则没有足够的信息执行部分回滚,因此ScalaSTM以精确嵌套模式重新启动整个事务。这种优化称为子消费。

所以发生的情况是:
  1. 整个事务尝试作为“扁平化”事务
  2. last设置为"outer"
  3. last设置为"inner"
  4. 打印"inner"
  5. 内部原子块抛出异常,外部块没有抛出异常
  6. ScalaSTM不知道如何回滚仅内部事务,因为它运行“扁平化”,因此回滚整个事务(last现在回到"none")并重新尝试“非扁平化”
  7. last设置为"outer"
  8. last设置为"inner"
  9. 打印"inner"
  10. 内部原子块抛出异常,外部块捕获异常
  11. 这次由于它是非扁平化的,它只能回滚内部块,并将last设置回"outer"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接