2011-11-24 10 views
8

Ho un attore che - nella sua essenza - mantiene un elenco di oggetti. Ha tre operazioni di base, un add, update e una remove (dove a volte la rimozione viene chiamata dal metodo add, ma a parte questo) e funziona con una singola raccolta. Ovviamente, a quella backing list si accede contemporaneamente, con l'aggiunta e la rimozione di chiamate che si interlacciano tra loro costantemente.Come gestire l'accesso concorrente a una collezione Scala?

La mia prima versione ha utilizzato un ListBuffer, ma ho letto da qualche parte che non è pensato per l'accesso simultaneo. Non ho ottenuto eccezioni di accesso simultanee, ma ho notato che trovare & rimuovere oggetti da esso non sempre funziona, probabilmente a causa della concorrenza.

Ero a metà della riscrittura per utilizzare un elenco var, ma rimuovere gli elementi dall'elenco immutabile predefinito di Scala è un po 'un dolore - e dubito che sia adatto per l'accesso simultaneo.

Quindi, domanda di base: quale tipo di raccolta devo utilizzare in una situazione di accesso concorrente e come viene utilizzata?

(forse secondario: è un attore in realtà un'entità multithread, o è solo la mia concezione sbagliata e lo fa elabora i messaggi uno alla volta in un singolo thread?)

(terziario: In Scala, quale la raccolta il tipo è migliore per inserimenti e accesso casuale (cancella/aggiorna)?)

Modifica: Per i rispondenti gentili: scusa la mia risposta in ritardo, sto facendo una brutta abitudine di scaricare una domanda su SO o mailing list, quindi passare al prossimo problema, dimenticando quello originale per il momento.

+3

Un attore elabora un messaggio alla volta. La concorrenza con gli attori deriva dall'elaborazione di messaggi asincroni, non da un attore che elabora contemporaneamente più messaggi. –

+2

Qual è il problema aziendale che stai cercando di risolvere? –

+0

@ViktorKlang: proverò a spiegarlo un po '. Un utente invia un oggetto modello chiamato NotificationPlan al server attraverso un servizio REST. Secondo NotificationPlan, viene generato un numero di oggetti di notifica, che a un certo punto in futuro verranno restituiti agli utenti (sotto forma di notifica push Apple). In questo caso, l'attore con l'elenco come descritto nella domanda mantiene un elenco di NotificationPlans in memoria, in modo che l'utente possa aggiornare o rimuovere il suo piano dopo averlo aggiunto inizialmente. – fwielstra

risposta

10

Dai un'occhiata ai tratti/classi di scala.collection.mutable.Synchronized *.

L'idea è di mescolare i tratti sincronizzati in normali collezioni mutabili per ottenere versioni sincronizzate di essi.

Ad esempio:

import scala.collection.mutable._ 
val syncSet = new HashSet[Int] with SynchronizedSet[Int] 
val syncArray = new ArrayBuffer[Int] with SynchronizedBuffer[Int] 
+11

Si prega di non fare questo, non scala. –

+9

A partire da Scala 2.11, i SyncronizedSet sono deprecati: la sincronizzazione tramite tratti è deprecata in quanto intrinsecamente inaffidabile. Considerare java.util.concurrent.ConcurrentHashMap [A, Unit] come alternativa. –

0

Che tipo di raccolta dovrei usare in una situazione di accesso simultaneo, e come si usa?

Vedere la risposta di @ hbatista.

è un attore in realtà un'entità multithread, o è che solo la mia concezione sbagliata e lo fa elabora i messaggi uno alla volta in un singolo thread

Il secondo (anche se il filo su cui i messaggi sono può essere modificato, quindi non memorizzare nulla nei dati locali del thread). È così che l'attore può mantenere invarianti sul suo stato.

4

Non è necessario sincronizzare lo stato degli attori. Lo scopo degli attori è di evitare programmazioni concorrenti difficili, soggette a errori e difficili da debugare.

Il modello di attore garantisce che l'attore consumerà i messaggi uno a uno e che non si avranno mai due messaggi di consumo thread per lo stesso attore.

3

Le collezioni immutabili di Scala sono adatte all'uso concomitante.

Come per gli attori, un paio di cose sono garantite come spiegato nella documentazione di Akka here.

  • l'attore invia regola: dove l'invio del messaggio a un attore avviene prima della ricezione dello stesso attore.
  • regola di elaborazione successiva attore: dove l'elaborazione di un messaggio avviene prima dell'elaborazione del messaggio successivo da parte dello stesso attore.

Non ha la garanzia che lo stesso thread elabora il messaggio successivo, ma si sta garantito che il messaggio corrente terminerà l'elaborazione prima del prossimo si parte, e anche che in un dato momento, solo un thread è eseguire il metodo di ricezione.

Così che si prende cura dello stato persistente di un determinato attore. Per quanto riguarda i dati condivisi, l'approccio migliore, a quanto ho capito, è quello di utilizzare strutture di dati immutabili e appoggiarsi il più possibile al modello di attore. Cioè, "non comunicare condividendo la memoria, condividere la memoria comunicando".

Problemi correlati