2010-04-27 9 views
6

Utilizzando Scala 2.8 RC1 o versione successiva, qual è il metodo migliore (più semplice e/o più diretto) da "sbirciare" ai messaggi in attesa nella cassetta postale di un attore (all'interno dello stesso attore metodo act()) per esaminare ciò che è in coda, senza dover reagire/ricevere i messaggi e/o disturbare in alcun modo il contenuto corrente della casella di posta.Il miglior metodo per dare un'occhiata alla casella postale di un attore Scala

Lo scopo di questo è che un attore possa determinare se è sicuro elaborare una richiesta di uscita determinando prima se uno qualsiasi dei messaggi di casella rimanenti è quello che deve essere elaborato, invece di farlo cadere fermando l'attore subito.

risposta

10

Non hai bisogno di sbirciare avanti. Basta tenere traccia del fatto che è stata richiesta un'uscita e utilizzare un reactWithin (0) per determinare quando la coda è vuota dopo che è stata richiesta un'uscita.

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

che dovrebbero uscita

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 
+0

Approccio molto interessante –

3

Questo suona come un'operazione pericolosa in generale, poiché se ci sono messaggi critici, l'attore che li processa potrebbe controllarne e trovarne nessuno, ma prima di uscire potrebbe esserne dato un altro da qualche altro thread.

Se sai per certo che questo non può accadere e non hai bisogno di molti switch di messaggi incredibilmente veloci, probabilmente scriverei un guard-actor che conta e tiene traccia dei messaggi critici ma altrimenti passa solo loro su un altro attore per la gestione.

Se non si desidera farlo, tenere presente che i dettagli degli interni dovrebbero cambiare e potrebbe essere necessario passare attraverso il codice sorgente per Actor, Reactor, MessageQueue, ecc. Per ottenere ciò che tu vuoi. Per ora, qualcosa di simile a questo dovrebbe funzionare (attenzione, non testata):

package scala.actors 
package myveryownstuff 
trait CriticalActor extends Actor { 
    def criticalAwaits(p: Any => Boolean) = { 
    synchronized { 
     drainSendBuffer(mailbox) 
     mailbox.get(0)(p).isDefined 
    } 
    } 
} 

Nota che dobbiamo mettere il tratto esteso nel pacchetto scala.actors, perché tutti i meccanismi interni della cassetta postale sono dichiarate private ai scala.actors pacchetto. (Questo è un buon avvertimento che dovresti fare attenzione prima di scherzare con gli interni.) Quindi aggiungiamo un nuovo metodo che accetta una funzione in grado di testare un messaggio critico e cercarlo usando il metodo integrato mailbox.get(n) che restituisce lo n messaggio che soddisfa alcuni predicati.

+0

io sicuramente non voglio manipolare i meccanismi interni del pacchetto scala.actors. Se risulta che non c'è modo di visualizzare i messaggi nella casella di posta, senza rimuoverli/elaborarli, allora dovrò accettarli e progettare di conseguenza. Attualmente, chiedo all'attore il conteggio dei messaggi rimasti nella casella di posta durante l'elaborazione del mio ExitMsg. Non ci dovrebbe essere nessuno, se il resto dell'applicazione sta chiudendo gli attori (alcuni dei quali generano messaggi per l'attore in queston) nell'ordine corretto. Quindi voglio una lettura non distruttiva solo per stampare il messaggio. –

+0

@scaling: 'mailbox.get (0)' è una lettura non distruttiva. Inoltre, in Scala 2.8, non è più possibile ottenere le dimensioni della casella di posta (ad esempio è un dettaglio di implementazione che è stato deciso non è completamente sicuro da esporre). L'idea del guardiano-attore potrebbe funzionare meglio per te. –

+0

Quindi, la linea di fondo è che non c'è modo di sbirciare nella cassetta postale usando l'API pubblica? La casella di posta corrente val è privata del pacchetto degli attori. –

Problemi correlati