2

1 から 20 までのローマ数字を受け入れることができる Akka ベースのオートマトンを作成しました。オートマトンは正常に動作しますが、完全な入力を処理した後、システムをシャットダウンしたいと考えています。

オートマトンがいつ終了するかわからないので、停止信号をいつ送信するかわかりません。これにより、システムが早期に停止し、オートマトンがタスクを完了できないという問題が発生します。アクター システムにまだ完了していないタスクがあるかどうかを確認する最善の方法は何ですか?

オートマトン:

import akka.actor.{Props, ActorSystem, ActorRef, Actor}

object RomanNumberAcceptor extends App {

  val allLegalNumbers = List("I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
    "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
  )

  val someWrongNumbers = List("IXX", "VV", "VX", "IIII", "IVV", "XXX", "XXI", "XXV", "IIX", "IIIV")

  for (number <- allLegalNumbers)
    State testNumber number

  for (number <- someWrongNumbers)
    State testNumber number

  // this stop signal is too early
  State.stop()
}

case class WordData(fullWord: String, remainder: String)

object State {
  val ErrorStateId: Int = -1

  private val data = Map(
    0 -> (Map('I' -> 1, 'V' -> 5, 'X' -> 10), false),
    1 -> (Map('I' -> 2, 'V' -> 4, 'X' -> 9), true),
    2 -> (Map('I' -> 3, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    3 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    4 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    5 -> (Map('I' -> 6, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    6 -> (Map('I' -> 2, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    9 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    10 -> (Map('I' -> 1, 'V' -> 5, 'X' -> 20), true),
    20 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    ErrorStateId -> (Map.empty[Char, Int], false)
  )

  val system = ActorSystem("RomanNumberAcceptor")

  val states: Map[Int, ActorRef] =
    for ((id, (map, accepted)) <- State.data)
      yield id -> system.actorOf(Props(State(id, map, accepted)), name = "s"+id)

  def testNumber(s: String) {
    states(0) ! WordData(s, s)
  }

  def stop() {
    for (a <- states.values)
      system stop a
    system.shutdown()
  }
}

case class State(id: Int, transMap: Map[Char, Int], accepted: Boolean) extends Actor {
  def receive = {
    case WordData(fullWord, _) if `id` == State.ErrorStateId =>
      println("Error: "+fullWord)
    case WordData(fullWord, remainder) if remainder.isEmpty =>
      println((if (accepted) "Success: " else "Error: ") + fullWord)
    case WordData(fullword, remainder) =>
      // maybe some heavy operation here
      (0 to 1e4.toInt).sum

      val nextAktor = transMap(remainder.head)
      State.states(nextAktor) ! WordData(fullword, remainder.tail)
  }
}
4

1 に答える 1

2

(以下は、ScalaのAkka以外のアクターを想定しています。これを調整して、Akkaで動作するようにする方法については、以下の編集を参照してください)

私の意見では、これに対する2つの可能な答えがあります。

  1. それは意味がありません。設計上、各州のアクターはそのように完成していません。これは、いつでもより多くの入力単語を受け取る可能性があり、それらを待っているだけだからです。終了するのは、チェックする単語を送信するコードです。

  2. メッセージに有用な値を返さないように見えWordDataますが、それでも戻り値を待つことができます。!!特に、 and!?演算子の使用法を確認する必要があります。それらをさまざまなアクターにチェーンすることで、それらが単語を処理するのを待つことができます。!?ただし、リエントラント状態はデッドロックにつながる可能性があるため、注意してください。

代わりに、その単語が受け入れられたかどうかを示すメッセージのBoolean回答を定義することをお勧めします。WordData次に、を使用!!して未来を取得でき(別の未来に依存し、別の未来に依存し、単語の長さなど)、testNumberメソッドはブロックして受け入れ値を取得できます。testNumber複数の単語を並行してテストする場合は、それ自体を返し、を呼び出す前にオブジェクトFuture内のこれらすべての先物をブロックすることもできます。Appstop

つまり、タイプ!!とともにアクターのオペレーターを検索する必要があります。Future

編集:混乱してすみません。drexinがコメントで指摘したように、これはAkka2で実際に変更されました。先物が取得されるようにメッセージを送信する方法については、先物のAkkaドキュメントを参照してください。

取得のみに関してNone:もちろん、アクターは何らかの意味のある値を返す必要があります(Boolean上記の提案のように)。アクターからの返信の送信方法については、ドキュメントをもう一度参照してください(基本的にはsender ! true)。もちろん、trueまたはを返すことができるのは基本ケースのみであることに注意してくださいfalse。別のアクターを呼び出すと、再び未来が得られ、デッドロックを回避するために、それを直接返す必要があります。

したがって、呼び出し側では、これらの先物を連続して待つ必要があります。つまり、最も外側の先物が別の先物に評価されるのを待つ必要があります。次に、この未来を待ち、以下同様に、未来ではないブール結果値を取得するまで待ちます。

アクター内から次のアクターを呼び出し、(未来自体ではなく)対応する未来の評価を返すという問題は、呼び出し側のアクターがブロックされることを意味します。!?したがって、これにより、従来のScalaアクターがブロッキング呼び出しを使用する場合と同じデッドロック動作が発生します。

于 2012-05-29T13:49:55.300 に答える