Sto lavorando a un'applicazione di rete haskell e utilizzo il pattern attore per gestire il multithreading. Una cosa che ho trovato è come archiviare per esempio un set di socket/handle client. Che, naturalmente, deve essere accessibile a tutti i thread e può cambiare quando i client si collegano/disattivano.Haskell - Mutazione basata su attore
Dato che io vengo dal mondo imperativo ho pensato a una sorta di lock-meccanismo, ma quando ho notato quanto brutto questo è ho pensato mutevolezza "puro", ben in realtà è una specie di pura:
import Control.Concurrent
import Control.Monad
import Network
import System.IO
import Data.List
import Data.Maybe
import System.Environment
import Control.Exception
newStorage :: (Eq a, Show a) => IO (Chan (String, Maybe (Chan [a]), Maybe a))
newStorage = do
q <- newChan
forkIO $ storage [] q
return q
newHandleStorage :: IO (Chan (String, Maybe (Chan [Handle]), Maybe Handle))
newHandleStorage = newStorage
storage :: (Eq a, Show a) => [a] -> Chan (String, Maybe (Chan [a]), Maybe a) -> IO()
storage s q = do
let loop = (`storage` q)
(req, reply, d) <- readChan q
print ("processing " ++ show(d))
case req of
"add" -> loop ((fromJust d) : s)
"remove" -> loop (delete (fromJust d) s)
"get" -> do
writeChan (fromJust reply) s
loop s
store s d = writeChan s ("add", Nothing, Just d)
unstore s d = writeChan s ("remove", Nothing, Just d)
request s = do
chan <- newChan
writeChan s ("get", Just chan, Nothing)
readChan chan
Il punto è che un thread (attore) gestisce un elenco di elementi e modifica l'elenco in base alle richieste in arrivo. Poiché i thread sono davvero economici, ho pensato che potesse essere un'alternativa funzionale davvero interessante.
Ovviamente questo è solo un prototipo (una rapida prova del concetto). Quindi la mia domanda è:
- È questo un modo "buono" della gestione delle variabili mutabili condivise (nel mondo attore)?
- Esiste già una libreria per questo modello? (Ho già cercato, ma non ho trovato nulla)
saluti, Chris
Se sei disposto ad esplorare alternative al modello dell'attore, ti suggerisco di provare la [Memoria transazionale software] di Haskell (https://en.wikipedia.org/wiki/Software_transactional_memory). È un meccanismo meraviglioso simile alle transazioni del database. Vedi [Capitolo 28] (http://book.realworldhaskell.org/read/software-transactional-memory.html) in The Real World Haskell. –
Tecnicamente un'ottima scelta, ma ho sentito che usare STM con un numero elevato di thread (un thread per client che è standard in haskell) e operazioni relativamente lunghe (eliminare un elemento da un elenco è O (n), ovviamente hash sets/le mappe potrebbero aiutare qui) potrebbe ridurre le prestazioni di STM di un numero elevato. E naturalmente il canale MVar potrebbe essere sostituito dal canale STM che significa utilizzare la migliore delle due tecniche. EDIT: Il pattern degli attori è generalmente molto bello in una situazione del genere, perché l'eliminazione o l'aggiunta di un elemento è O (1) (è sufficiente inviare un messaggio) Il lavoro effettivo è fatto in una discussione ... – Kr0e
Hai ragione. Con STM può succedere che le transazioni vengano riavviate più volte, portando a prestazioni ridotte. Ma se le tue operazioni sincronizzate richiedono molto tempo, puoi anche avere problemi simili con gli attori - se ci sono più messaggi di quanti ne possa gestire, il suo stato resterà indietro rispetto alla realtà. Quindi usare gli alberi bilanciati ('Map' /' Set') o i set di hash 'ST/IO' sarebbero sicuramente d'aiuto. –