并发程序中的Scala模式匹配

Ore*_*ren 5 concurrency scala pattern-matching

我是Scala的新手,我想编写一些带有模式匹配的多线程代码,我想知道我是否可以将模式匹配代码视为原子.

例如:

abstract class MyPoint
case class OneDim(x : Int) extends MyPoint
case class TwoDim(x : Int, y : Int) extends MyPoint

var global_point : MyPoint = new OneDim(7)

spawn {
    Thread.sleep(scala.util.Random.nextInt(100))
    global_point = new TwoDim(3, 9)
}
Thread.sleep(scala.util.Random.nextInt(100))

match global_point {
    case TwoDim(_, _) => println("Two Dim")
    case OneDim(_) => println("One Dim")
}
Run Code Online (Sandbox Code Playgroud)

是否有可能执行如下:

  1. 主线程到达"match global_point"代码,发现*global_point*不是TwoDim类型并且暂停(返回调度程序).
  2. 生成的线程将*global_point*更改为TwoDim类型
  3. 主线程返回,发现*global_point*不是OneDim类型,认为没有与*global_point*匹配并引发NoMatch异常.

Scala内部是否避免了这种执行?如果确实如此,那怎么样?匹配是否拍摄对象的快照,然后尝试将其与模式匹配?快照深度是否有限制(匹配模式可以是复杂的和嵌套的)?

Mal*_*off 4

这并不是规范中确凿的证据,但它说明了编译器为您所做的一些事情,这应该允许您将一些匹配块视为原子的 -但绝对不是全部。如果您自己同步代码,或者使用不可变对象,则会安全得多。

平面示例

如果您使用以下命令运行以下脚本scala -print

var m: Option[String] = _

m match {
  case Some(s) => "Some: " + s
  case None => "None"
}
Run Code Online (Sandbox Code Playgroud)

您将看到编译器创建的脱糖中间代码(为了简洁起见,我删除了一些代码):

final class Main$$anon$1 extends java.lang.Object {
  private[this] var m: Option = _;

  private <accessor> def m(): Option = Main$$anon$1.this.m;

  def this(): anonymous class Main$$anon$1 = {
    <synthetic> val temp1: Option = Main$$anon$1.this.m();

    if (temp1.$isInstanceOf[Some]()) {
      "Some: ".+(temp1.$asInstanceOf[Some]().x())
    else if (scala.this.None.==(temp1))
      "None"
    else
      throw new MatchError(temp1)
  }
}
Run Code Online (Sandbox Code Playgroud)

引用的可能共享对象获取m本地别名temp1,因此如果m在后台更改它指向另一个对象,则匹配仍然发生在m指向的旧对象上。因此,您上面描述的情况(更改global_point为指向 aTwoDim而不是 a OneDim)不是问题。

嵌套示例

通常的情况似乎是编译器为绑定在匹配情况的保护中的所有对象创建本地别名,但它不会创建深层副本!对于以下脚本:

case class X(var f: Int, var x: X)

var x = new X(-1, new X(1, null))

x match {
  case X(f, ix) if f >  0 || ix.f > 0  => "gt0"
  case X(f, ix) if f <= 0 || ix.f <= 0 => "lte0"
}
Run Code Online (Sandbox Code Playgroud)

编译器创建这个中间代码:

private[this] var x: anonymous class Main$$anon$1$X = _;

private <accessor> def x(): anonymous class Main$$anon$1$X = Main$$anon$1.this.x;

final <synthetic> private[this] def gd2$1(x$1: Int, x$2: anonymous class Main$$anon$1$X): Boolean = x$1.>(0).||(x$2.f().>(0));

final <synthetic> private[this] def gd3$1(x$1: Int, x$2: anonymous class Main$$anon$1$X): Boolean = x$1.<=(0).||(x$2.f().<=(0));

def this(): anonymous class Main$$anon$1 = {
  <synthetic> val temp6: anonymous class Main$$anon$1$X = Main$$anon$1.this.x();

  if (temp6.ne(null)) {
    <synthetic> val temp7: Int = temp6.f();
    <synthetic> val temp8: anonymous class Main$$anon$1$X = temp6.x();
    
    if (Main$$anon$1.this.gd2$1(temp7, temp8))
      "gt0"
    else if (Main$$anon$1.this.gd3$1(temp7, temp8))
      "lte0"
    else
      throw new MatchError(temp6)
  } else
    throw new MatchError(temp6)
}
Run Code Online (Sandbox Code Playgroud)

在这里,编译器为x您匹配的对象及其两个子对象x.f(绑定到f)和x.x(绑定到ix)创建本地别名,但不为 创建本地别名ix.f。因此,如果您匹配的结构是深度嵌套的,并且您的情况依赖于您未在本地绑定的嵌套对象,则可能会发生竞争条件。众所周知,由于墨菲定律,将会发生。