5

私は 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")
}

実行が次のようになる可能性はありますか?

  1. メイン スレッドは「match global_point」コードに到達し、*global_point* が TwoDim 型ではないことがわかり、一時停止します (スケジューラに戻ります)。
  2. 生成されたスレッドは *global_point* を TwoDim 型に変更します
  3. メイン スレッドが戻り、 *global_point* がOneDim型ではないことを検出し、 *global_point*に一致するものはないと判断し、NoMatch 例外を発生させます。

この種の実行は Scala によって内部的に回避されますか? もしそうなら、どうやって?マッチングはオブジェクトのスナップショットを取得し、それをパターンと照合しようとしますか? スナップショットの深さに制限はありますか (一致パターンは複雑でネストされている可能性があります)?

4

3 に答える 3

4

これは仕様からの確固たる証拠ではありませんが、コンパイラーが行ういくつかのことを示しており、これにより、いくつかの一致ブロックをアトミックと見なすことができますが、すべてではないことは間違いありません。コードを自分で同期するか、不変オブジェクトを使用すると、より安全になります。

フラットの例

で次のスクリプトを実行するとscala -print:

var m: Option[String] = _

m match {
  case Some(s) => "Some: " + s
  case None => "None"
}

コンパイラによって作成された desugared 中間コードが表示されます (簡潔にするために一部のコードを削除しました)。

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)
  }
}

によって参照される可能性のある共有オブジェクトmは、ローカル エイリアス を取得するtemp1ため、別のオブジェクトを指すように がバックグラウンドで変更された場合でも、指してmいる古いオブジェクトで一致が行われますm。したがって、上記の状況( aではなくglobal_pointa を指すように変更する)は問題ではありません。TwoDimOneDim

ネストされた例

一般的に、コンパイラは一致ケースのガードにバインドされているすべてのオブジェクトにローカル エイリアスを作成しますが、ディープ コピーは作成しません。次のスクリプトの場合:

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"
}

コンパイラは次の中間コードを作成します。

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)
}

ここで、コンパイラは、x一致するオブジェクトと、その 2 つのサブオブジェクトx.f( にバインドf) およびx.x( にバインド) のローカル エイリアスを作成しますixが、ix.f. したがって、一致する構造が深くネストされていて、ローカルにバインドしていないネストされたオブジェクトにケースが依存している場合、競合状態が発生する可能性があります。そして、マーフィーの法則により、誰もが知っているようになります。

于 2012-08-14T07:18:13.163 に答える
1

Scalaでは、同時実行のために推奨されるソリューションはActorsを使用することです。これらActorの場合、動作を追加するにはパターンマッチングを使用する必要があります。私はあなたと同じようなスカラを使って例を作りましたActor

import scala.actors.Actor
import scala.actors.Actor._

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

case object Next
case object End

class OneDimSlave extends Actor {

  def act() {
    println("Starting OneDimSlave")
  loop {
        receive {
          case End => println("Stoping OneDimSlave"); exit()
          case Next => sender ! OneDim(scala.util.Random.nextInt(100))
        }
      }
}

}

class TwoDimSlave extends Actor {

  def act() {
    println("Starting TwoDimSlave")
  loop {
    receive {
      case End => println("Stopping TwoDimSlave"); exit()
      case Next => sender ! TwoDim(scala.util.Random.nextInt(100),scala.util.Random.nextInt(100))
    }
  }
}

}

class Master extends Actor {

  val oneDimSlave = new OneDimSlave
  val twoDimSlave = new TwoDimSlave

  oneDimSlave.start
  twoDimSlave.start

  def act {
    println("Starting Master")
for (_ <- 0 to 9) { oneDimSlave ! Next; twoDimSlave ! Next }
var count = 0
loop {
  if (count >= 20) { oneDimSlave ! End; twoDimSlave ! End; println("Stopping Master"); exit() }
  receive {
    case _ :OneDim => count += 1; println(count + ". One Dim")
    case _: TwoDim => count += 1; println(count + ". Two Dim")
  }
}
  }

}

object Main extends App {
  val master = new Master
  master.start
}

まず、メッセージを作成しました。sの間で不変を送信するだけなActorので、caseオブジェクトを使用しました。答えをラップすることもできますが、この場合は必要ありません。MyPoint次に、2種類のスレーブを作成しました。それらは、を受け取った場合に新しいスレーブを返すだけです。そして最後にマスターを作成しました。マスターは、各種類の2つのスレーブを初期化し、それらを開始します。その後、彼はそれぞれに10回送信Nextします。次に、彼は応答を受け取り、それらを印刷します。すべての応答を受信すると、マスターはEndすべてのスレーブに送信して終了します。次の出力が得られました。

Starting TwoDimSlave
Starting OneDimSlave
Starting Master
1. One Dim
2. Two Dim
3. Two Dim
4. Two Dim
5. Two Dim
6. Two Dim
7. Two Dim
8. Two Dim
9. Two Dim
10. Two Dim
11. Two Dim
12. One Dim
13. One Dim
14. One Dim
15. One Dim
16. One Dim
17. One Dim
18. One Dim
19. One Dim
20. One Dim
Stopping Master
Stopping OneDimSlave
Stopping TwoDimSlave
于 2012-08-14T07:13:20.980 に答える
1

パターン マッチング用に生成されたコードで、何らかのロックやスナップショットが行われているとは思いません。もしそうなら、それは確かに言語仕様に記載されているでしょう。そうは言っても、パターン マッチング コードをメソッドに入れると、メソッドが渡される参照がメソッドの実行中に変更されないことを少なくとも確認できます。TwoDim と OneDim も不変であれば、スレッド セーフについて心配する必要はありません。それらが不変でなくても、可変フィールドの 1 つに一致しない限り問題ではありません。

于 2012-08-14T07:01:27.023 に答える