2009-06-17 11 views
27

The reply a una mia recente domanda ha indicato che un attore ha elaborato i suoi messaggi uno alla volta. È vero? Non vedo niente che dice esplicitamente che (in Programmazione in Scala), che contiene il seguente frammento (pp. 593)Gli attori di Scala possono elaborare più messaggi contemporaneamente?

Se [il metodo react] trova un messaggio che può essere gestito, [it] sarà programmare la gestione di quel messaggio per l'esecuzione successiva e un'eccezione

(enfasi mia). Due correlate (e reciprocamente esclusivi) domande:

  1. Ipotizzando un attore potrebbe elaborare più messaggi simulatenously, come posso forzare un attore per elaborare i messaggi 1 alla volta (se questo è quello che voglio fare)? (Utilizzando receive?)
  2. Ipotizzando un attore elabora i messaggi uno alla volta, come farei meglio implementare un attore che di fatto potrebbe segnalazioni di processo in concomitanza

edit: fare un po 'di prove sembra fai notare che ho torto e che gli attori sono davvero sequenziali. Quindi è la domanda n. 2 alla quale ho bisogno di rispondere

+5

Davvero non vedo come questa domanda giustifica un downvote. Se riesco a leggere un intero capitolo sugli attori in Programmazione in Scala e non sono sicuro della risposta, è una domanda completamente valida. –

+0

Un singolo attore elabora i messaggi in modo sequenziale perché l'idea è che si possa utilizzare un attore per mediare l'accesso a una risorsa (altrimenti) condivisa e non sarà richiesta alcuna altra sincronizzazione sulla risorsa condivisa.Se i messaggi possono essere elaborati in sequenza, è necessario utilizzare i blocchi all'interno degli attori per garantire che non si verifichino errori di sincronizzazione. –

risposta

26

attori del processo di un messaggio alla volta. Lo schema classico per elaborare più messaggi è quello di avere un attore coordinatore per un pool di attori consumatori. Se si utilizza reagire, il pool di consumatori può essere grande ma utilizzerà solo un numero limitato di thread JVM. Ecco un esempio in cui creo un pool di 10 consumatori e un coordinatore in primo piano per loro.

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

case class Request(sender : Actor, payload : String) 
case class Ready(sender : Actor) 
case class Result(result : String) 
case object Stop 

def consumer(n : Int) = actor { 
    loop { 
    react { 
     case Ready(sender) => 
     sender ! Ready(self) 
     case Request(sender, payload) => 
     println("request to consumer " + n + " with " + payload) 
     // some silly computation so the process takes awhile 
     val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString 
     sender ! Result(result) 
     println("consumer " + n + " is done processing " + result) 
     case Stop => exit 
    } 
    } 
} 

// a pool of 10 consumers 
val consumers = for (n <- 0 to 10) yield consumer(n) 

val coordinator = actor { 
    loop { 
    react { 
     case msg @ Request(sender, payload) => 
      consumers foreach {_ ! Ready(self)} 
      react { 
       // send the request to the first available consumer 
       case Ready(consumer) => consumer ! msg 
      } 
     case Stop => 
      consumers foreach {_ ! Stop} 
      exit 
    } 
    } 
} 

// a little test loop - note that it's not doing anything with the results or telling the coordinator to stop 
for (i <- 0 to 1000) coordinator ! Request(self, i.toString) 

Questo codice consente di verificare quale utente è disponibile e invia una richiesta a tale utente. Le alternative sono assegnate solo in modo casuale ai consumatori o per utilizzare uno scheduler round robin.

A seconda di cosa stai facendo, potresti essere meglio servito con i Futures di Scala. Per esempio, se non hai davvero bisogno di attori, tutti i suddetti macchinari potrebbero essere scritti come

import scala.actors.Futures._ 

def transform(payload : String) = {  
    val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString 
    println("transformed " + payload + " to " + result) 
    result 
} 

val results = for (i <- 0 to 1000) yield future(transform(i.toString)) 
+0

Puoi spiegare cosa significa Ready? –

+0

Pronto in questo esempio è una classe di caso che funge da messaggio da passare. Il coordinatore invia un messaggio Pronto al consumatore che in pratica significa "sei pronto?" e il consumatore risponde con un messaggio pronto che significa "sì". Il primo utente a rispondere è quindi passato la richiesta. – harmanjd

0

Se vuoi fare più cose, dovresti usare più attori. L'intera ragione per usare gli attori è dividere il lavoro tra più processi indipendenti.

+0

Questa è una risposta inutile. Diciamo che ho un attore che legge gli eventi da elaborare e li invia a qualche altro attore per essere processato. Tranne che in realtà, più eventi possono essere elaborati simultaneamente, anche se il codice di elaborazione effettivo è esattamente lo stesso (che quindi scriverei in un unico posto, cioè in 1 attore). Come posso permettere agli attori di "replicare" (o creare un pool) attori di questi (identici) attori in modo da poter utilizzare al meglio le mie risorse di programmazione? –

+0

Intendo "utilizzo ottimale delle risorse del mio processore" –

+1

La mia risposta non è stata utile perché la tua domanda non è stata correttamente inquadrata. Un attore è un processo non un pezzo di codice. Proprio come un oggetto è un'istanza di una classe. Non hai chiesto come creare più attori per gestire più messaggi. Volevi sapere come ottenere un attore per elaborare più messaggi contemporaneamente. La mia risposta è stata un tentativo di chiarire quello che ho percepito come il tuo fraintendimento del modello dell'attore. – mamboking

6

Penso che la risposta sia che uno Actor non può gestire i messaggi in modo sincrono. Se si dispone di un Actor che dovrebbe essere l'ascolto di messaggi in cui questi messaggi possono essere gestite in modo asincrono, allora potrebbe essere scritto in questo modo:

val actor_ = actor { 

    loop { 
    react { 
     case msg => 
     //create a new actor to execute the work. The framework can then 
     //manage the resources effectively 
     actor { 
      //do work here 
     } 
     } 
    } 
    } 
Problemi correlati