Actor &       STM
  じゅんいち☆かとう
    @j5ik2o
並行処理 難しい
何が難しいか
スレッドセーフ
 が難しい
原子性 可視性 順序性
スレッドと通信する
ことってあるよねー
Q1.スレッドを止められるか...
public class StopThread implements Runnable {
 private boolean terminate;
 public void run() {
   while (! terminate) {
      // なにかの処理
      }
    }
    public void setTerminate(boolean terminate) {
      this. terminate = terminate;
    }
}
HotSpot ServerVMなら...

   if (!terminate) {
         while(true){
           // なにかの処理
       }
   }
スレッド単位の
セマンティクス
こう思ったら間違いのもと

スレッドA   terminate ← true



                           terminate



スレッドB   true ← terminate
こう思ったら間違いのもと

スレッドA   terminate ← true




スレッドB   false ← terminate
        true ← terminate
メモリモデルはスレッド単位

スレッドA   terminate ← true    terminate(true)




スレッドB   false ← terminate   terminate(false)
メモリモデルはスレッド単位

スレッドA   terminate ← true    terminate(true)



                            大域のterminate



スレッドB   false ← terminate   terminate(false)
メモリモデルはスレッド単位

スレッドA   terminate ← true    terminate(true)


                同期化しないと
                  見えない      大域のterminate



スレッドB   false ← terminate
        true ← terminate    terminate(false)
                            terminate(true)
これは可視性の問題




 http://www.flickr.com/photos/teegardin/5547069087/sizes/m/in/photostream/
volatileェ...
public class StopThread implements Runnable {
  private volatile boolean terminate;
  public void run() {
      // (2) Volatile Load
     while (! terminate) {
         // スレッドで行う処理
     }
  }
  public void setTerminate(boolean terminate) {
    // (1) Volatile Store
    this. terminate = terminate;
  }
}
Q2.x,yの結果はどうなる?
x = 0; y = 0; a = 0; b = 0;
class AThread extend          class BThread extend
Runnable {                    Runnable {
  @Override                     @Override
  public void run() {           public void run() {
    a = 1;                        b = 1;
    x = b;                        y = a;
  }                             }
}                             }
(1,1),(1,0),
(0,1),(0,0)
何回目で発生したか   (x   ,   y)
004590      (1   ,   1)
005606      (0   ,   0)
045450      (0   ,   1)
045451      (1   ,   0)
105747      (0   ,   0)
129356      (0   ,   0)
これは順序性の問題




 http://www.flickr.com/photos/wahlander/3808866872/sizes/m/in/photostream/
(可変な)変数の共有と
 ロック争奪に基づく
   共有データ・
   ロックモデル
• 読み書きしようとしているデータが他
 のスレッドから読み書きされないか。
 どのようなロックがかけられている
 か。

• メソッドを呼び出す際にどのような
 ロックを取得するか。その呼び出しに
 よってデットロックがないか。
Scala with Actor
共有なしの
メッセージ交換モデル
こんな感じ...
メッセージを受信するActor
class MyActor extends Actor {
   def act() = { // Runnable#run相当
     loop { // while(true)の簡略表現
       receive { // メッセージの待ち受け(ブロックする)
         case "end" => exit
         case msg: String => println(msg)
       }
     }
   }
}
val myactor = new MyActor
myactor.start()
// 文字列を渡す
myactor ! “Hello”
// 終了メッセージを渡す
myactor ! “end”
返事を返すActor
val myactor = actor {
  loop {
    receive {
      case "end" => exit
      case "Hello" => reply("World")
    }
  }
}
// 戻り値を取得(返事が戻るまでブロック)
val result = myactor !? “Hello”
Futureを返すActor
val myactor = actor {
   loop {
     receive {
       case "end" => exit
       case numbers: List[Int] =>
        重いソート処理
        reply(result)
     }
   }
}
// futureパターン
val future = myactor !! largeNumbers
while(!future.isSet){ Thread.sleep(1000) }
val result = future()
もっと簡潔に
val myactor = actor {
  loop {
    receive {
      case "end" => exit
      case msg: String => println(msg)
    }
  }
}
// 文字列を渡す(ブロックしない)
myactor ! “Hello”
// 終了メッセージを渡す (ブロックしない)
myactor ! “end”
reactはスレッドを節約する
val myactor = actor {
  loop {
    react {
       case "end" => exit
       case numbers: List[Int] =>
        重いソート処理
        reply(result)
    }
    // 制御を返さないので,ここは実行できない
  }
}
リソースを共有しない利点
class MyActor extends Actor {
   def act() = { // Runnable#run相当
     loop { // while(true)の簡略表現
       receive { // メッセージの待ち受け(ブロックする)
         case "end" => exit
         case msg: String => println(msg)
       }
     }
   }
}                             シングルスレッド脳
val myactor = new MyActor
myactor.start()                 で考えればよい
// 文字列を渡す
myactor ! “Hello”
// 終了メッセージを渡す
myactor ! “end”
mutalbleなメッセージはやめよう
class Money(var amount:BigDecimal,     val actorA = new MyActor
  var currency: Currency) {
  def plus(add: Money):Unit = {        val actorB = new MyActor
     var a = amount                    val money = new Money(10,
     a = a + add.amount
     amount = a                        Currency.getInstance("JPY"
  }                                    ))
  // いろいろ省略
                                       actorA ! ("set", money)
}                                      actorB ! ("set", money)
class MyActor extends Actor {          actorA ! ("add", money)
  var money: Money = _                 actorB ! ("add", money)
  def act() = {
    loop {
      react {
        case "end" => exit
        case ("set", m) => money = m
        case ("add", m) =>
         money.plus(m)
      }
    }
  }
}
mutalbleなメッセージはやめよう
class Money(var amount:BigDecimal, val actorA = new MyActor
  var currency: Currency) {
  def plus(add: Money):Unit = {    val actorB = new MyActor
     var a = amount                val money = new Money(10,
     a = a + add.amount
     amount = a                    Currency.getInstance("JPY"
  }                                ))
  // いろいろ省略
                        amount 参照の可視性
                                   actorA ! ("set", money)
}                                  actorB ! ("set", money)
                        plusメソッドの原子性
class MyActor extends Actor {          actorA ! ("add", money)
  var money: Money = _                 actorB ! ("add", money)
  def act() = {
    loop {
      react {
        case "end" => exit
        case ("set", m) => money = m
        case ("add", m) =>
         money.plus(m)
      }
    }
  }
}
STM
(Software Transcational
      Memoery)
スレッドセーフ?
    class Sequence {
    private var value = 0
    def getValue = value
    // ↓ スレッドセーフか?
    def getAndIncrement() = {
     value += 1 // Java だと value++
        value
    }
}
スレッドセーフ?
二つのスレッドから


sequence.getAndIncrement()


を1000回呼び出したら、最後
に2000になるか
0182   1998
0848   1998
3438   1998
3714   1998
3950   1998
レースコンディション
これは原子性の問題




 http://www.flickr.com/photos/swellzombie/4303343824/sizes/m/in/photostream/
ロックで同期化
class Sequence {
  private var value = 0
  def getValue = value
   // ↓ ロックが必要
  def getAndIncrement() = synchronized {
    value += 1
    value
  }
}
STM(Ref)
class Sequence {
  private var value = Ref(0)
  def getValue = value.get
  def getAndIncrement() = atomic {
    value alter (_ + 1)
  }
}
class Ref[T] ...
def alter(f: T T): T = {
    val value = f(get)
    set(value)
    value
}

Actor&stm

Editor's Notes

  • #2 \n
  • #3 \n
  • #4 \n
  • #5 \n
  • #6 \n
  • #7 \n
  • #8 巻き上げ。最適化していいことになっているから、\n
  • #9 \n
  • #10 スレッドAで書き込んだら、次のスレッドBで読み込めるとは限らない。\n
  • #11 スレッドAで書き込んだら、次のスレッドBで読み込めるとは限らない。\n
  • #12 スレッドAで書き込んだら、次のスレッドBで読み込めるとは限らない。\n
  • #13 スレッドAで書き込んだら、次のスレッドBで読み込めるとは限らない。\n
  • #14 \n
  • #15 \n
  • #16 \n
  • #17 \n
  • #18 \n
  • #19 \n
  • #20 \n
  • #21 \n
  • #22 \n
  • #23 (1)のVolatile Storeの結果が(2)のVolatile Loadで見えることが保証される。volatileでなくてもAtomicBooleanでもよいですね。可変な変数はいろいろややこしい。\n
  • #24 二つのスレッドが同時で動いたとしたら、x, yの結果はどうなりますか。\n
  • #25 同期化したメモリアクセスではないので、スレッドのスケジューリング次第で運任せ。\n
  • #26 結構な頻度でおこります。\n
  • #27 理屈はこうです。スレッドAの命令がリオーダーされた場合は0,0となります。\n
  • #28 \n
  • #29 \n
  • #30 このように、そもそも人間のメンタルモデルから遠いノウハウを要求されてしまうのが、共有データ・ロックモデルです。ですから、本当にスレッドセーフで、スループットの出る マルチスレッドプログラミングができるプログラマは非常に少ないですし、これから目指すのも難しいということが言えます。\n
  • #31 \n
  • #32 共有データロックモデルは簡単にデットロックを引き起こしてしまう可能性があるが、アクターは回避しやすい。これからは並行処理は、アクターから考えるべきです。\n
  • #33 メッセージパッシングによる抽象度の高い並行処理モデルが、アクターモデルです。アクターとしてはErlangが有名。アクターはスレッドとメールボックスが一体になったようなオブジェクトです。アクターに何かを依頼するときはメッセージを送信します。アクターで受信したメッセージはキューで管理され、キューから取り出されたメッセージに応じた処理が実行されます。\n
  • #34 リソースを共有しないので、シングルスレッドモデルでよいのです。\n
  • #35 \n
  • #36 \n
  • #37 actorという関数を引数に取るファクトリメソッド。\n
  • #38 receiveはブロックします。実行効率が悪い。もっと実行効率を上げるにはreactを使います。\n通常はアクターにはスレッドが割当てられます。 スレッドはたくさん作るとOutOfMemoryErrorを引き起こします。Javaで学んだようにスレッドをプーリングしてスレッドを再利用しなければなりません。reactはブロックせずに、スレッドを再利用します。ほとんどreceiveと同じことができますが、メッセージを処理しても制御を返しません。カレントスレッドは、次のアクターのために再利用されます。プログラム内のすべてのアクターがreactを使うなら1つのスレッド実行しようとします。ただし、コアの数によります。\n
  • #39 リソースを共有しないので、メッセージだけで通信していれば、シングルスレッドモデルでよいのです。\n
  • #40 しかし、落とし穴があってメッセージオブジェクトがmutableだとつらいことになります。とにかくImmutableにしないといろいろ面倒なことを考慮しないと、死にます。\n
  • #41 \n
  • #42 同時複数のスレッドが同じ値を取得し、立て続けに加算した場合想定が崩れる。リードモディファイライトが、、\n
  • #43 同時複数のスレッドが同じ値を取得し、立て続けに加算した場合想定が崩れる。リードモディファイライトが、、\n
  • #44 結構な頻度でおこります。\n
  • #45 同時複数のスレッドが同じ値を取得し、立て続けに加算した場合想定が崩れる。リードモディファイライトが、、\n
  • #46 \n
  • #47 同時複数のスレッドが同じ値を取得し、立て続けに加算した場合想定が崩れる。リードモディファイライトが、、\n
  • #48 ロックを取得しないでアトミックな操作ができる。実行中のメモリIOを記録し、共有メモリに対しての変更を行う。\nロックがあればスレッド間の調停を行うが、この場合の整合性の検証は書き込み側ではなく、読み込み側に責任がある。トランザクション完了後に読み込み側で、他のスレッドで変更されていないかを検証させる。問題なければコミット、問題あればロールバックされてatomicの式は最初からやり直しになる。\n何がよいのかは、ロックコストを削減と、プログラムがわかりやすくなる。シングルスレッドで考慮すればいいか。デッドロック全く起きないか、トランザクションマネージャによって管理されているので安全です。\n\n