Mit Scala 2.8 RC1 oder neuer ist es die beste (einfachste und/oder direkteste) Methode, um die wartenden Nachrichten im Postfach eines Akteurs zu sehen (vom selben Akteur) act() - Methode), um zu prüfen, was sich in der Warteschlange befindet, ohne auf die Nachrichten reagieren und/oder den aktuellen Inhalt der Mailbox in irgendeiner Weise stören zu müssen. Der Zweck ist, dass ein Akteur bestimmen kann, ob es sicher ist, eine Anfrage zum Beenden zu verarbeiten, indem zuerst bestimmt wird, ob irgendwelche der verbleibenden Mailboxnachrichten solche sind, die verarbeitet werden müssen, anstatt einfach durch Stoppen des Aktors fallengelassen zu werden sofort.Die beste Methode, um in eine Mailbox von Scala Actor zu schauen
Antwort
Sie müssen nicht vorausschauen. Verfolgen Sie einfach die Tatsache, dass ein Exit angefordert wurde, und verwenden Sie eine reactWithin (0), um festzustellen, wann die Warteschlange leer ist, nachdem ein Exit angefordert wurde.
import scala.actors._
sealed case class Message
case object Exit extends Message
case class Unimportant(n:Int) extends Message
case class Important(n:Int) extends Message
class SafeExitingActor extends Actor {
def act : Nothing = react {
case Exit => {
println("exit requested, clearing the queue")
exitRequested
}
case message => {
processMessage(message, false)
act
}
}
// reactWithin(0) gives a TIMEOUT as soon as the mailbox is empty
def exitRequested : Nothing = reactWithin(0) {
case Exit => {
println("extra exit requested, ignoring")
exitRequested // already know about the exit, keep processing
}
case TIMEOUT => {
println("timeout, queue is empty, shutting down")
exit // TIMEOUT so nothing more to process, we can shut down
}
case message => {
processMessage(message, true)
exitRequested
}
}
// process is a separate method to avoid duplicating in act and exitRequested
def processMessage(message : Any, importantOnly : Boolean) = {
message match {
case Unimportant(n) if !importantOnly => println("Unimportant " + n)
case Unimportant(n) =>() // do nothing
case Important(n) => println("Important! " + n)
}
Thread sleep 100 // sleep a little to ensure mailbox backlog
}
}
object TestProcessing {
def main(args : Array[String]) {
val actor = new SafeExitingActor()
actor.start()
for (i <- 1 to 10) {
actor ! Unimportant(i)
actor ! Important(i)
}
actor ! Exit
for (i <- 11 to 20) {
actor ! Unimportant(i)
actor ! Important(i)
}
actor ! Exit
actor ! Important(100)
}
}
Das sollte eine Ausgabe
Unimportant 1
Important! 1
Unimportant 2
Important! 2
Unimportant 3
Important! 3
Unimportant 4
Important! 4
Unimportant 5
Important! 5
Unimportant 6
Important! 6
Unimportant 7
Important! 7
Unimportant 8
Important! 8
Unimportant 9
Important! 9
Unimportant 10
Important! 10
exit requested, clearing the queue
Important! 11
Important! 12
Important! 13
Important! 14
Important! 15
Important! 16
Important! 17
Important! 18
Important! 19
Important! 20
extra exit requested, ignoring
Important! 100
timeout, queue is empty, shutting down
Das hört sich im Allgemeinen nach einer gefährlichen Operation an, denn wenn es kritische Nachrichten gibt, kann der sie verarbeitende Akteur prüfen und keine finden, aber vor dem Beenden kann ein anderer von einem anderen Thread gegeben werden.
Wenn Sie sicher wissen, dass dies nicht passieren kann, und Sie nicht viele unglaublich schnelle Nachrichtenschalter benötigen, würde ich wahrscheinlich einen Wächter-Schauspieler schreiben, der kritische Nachrichten zählt und verfolgt, aber ansonsten einfach vergeht sie an einen anderen Schauspieler für die Handhabung.
Wenn Sie das nicht tun möchten, sollten Sie daran denken, dass sich die Details der Interna ändern sollten und Sie möglicherweise den Quellcode für Actor, Reactor, MessageQueue usw. durchgehen müssen, um was zu bekommen Sie wollen. Vorerst soll in etwa so funktioniert (Warnung, nicht getestet):
package scala.actors
package myveryownstuff
trait CriticalActor extends Actor {
def criticalAwaits(p: Any => Boolean) = {
synchronized {
drainSendBuffer(mailbox)
mailbox.get(0)(p).isDefined
}
}
}
Beachten Sie, dass wir die erweiterte Eigenschaft in dem scala.actors Paket zu platzieren, weil alle Mailbox-Interna zu dem scala.actors privat deklariert sind Paket. (Dies ist eine gute Warnung, dass Sie vorsichtig sein sollten, bevor Sie sich mit den Interna herumschlagen.) Wir fügen dann eine neue Methode hinzu, die eine Funktion verwendet, die eine kritische Nachricht testen und mit der integrierten mailbox.get(n)
-Methode suchen kann, die n
zurückgibt Nachricht, die ein Prädikat erfüllt.
ich definitiv nicht will, die Interna des scala.actors Pakets zu manipulieren. Wenn sich herausstellt, dass es keine Möglichkeit gibt, die Nachrichten im Postfach anzuzeigen, ohne sie zu entfernen/zu verarbeiten, muss ich das akzeptieren und entsprechend gestalten. Derzeit frage ich den Schauspieler für die Anzahl der Nachrichten in der Mailbox während der Verarbeitung meiner eigenen ExitMsg. Es sollte keine geben, wenn der Rest der Anwendung die Akteure (von denen einige Nachrichten für den Akteur in Queston generieren) in der richtigen Reihenfolge herunterfährt. Also möchte ich ein zerstörungsfreies Lesen, nur um die Nachricht auszudrucken. –
@scaling: 'mailbox.get (0)' ist ein nicht-destruktives Lesen. Außerdem können Sie in Scala 2.8 nicht mehr die Größe des Postfachs ermitteln (d. H. Es ist ein Implementierungsdetail, das nicht vollständig sicher bestimmt werden konnte). Die Idee des Wächter-Schauspielers könnte für dich besser funktionieren. –
Also, das Endergebnis ist, dass es keine Möglichkeit gibt, mit der öffentlichen API in das Postfach zu gucken? Das aktuelle Postfach ist für das Akteurspaket privat. –
Sehr interessanter Ansatz –