Ich arbeite an einer Haskell Netzwerk-Anwendung und ich benutze das Aktor-Muster, um Multithreading zu verwalten. Eine Sache, die ich fand, ist, wie man zum Beispiel eine Reihe von Client-Sockets/Handles speichert. Diese müssen natürlich für alle Threads zugänglich sein und können sich ändern, wenn sich Clients an-/abmelden.Haskell - Aktor basierte Wandelbarkeit
Da ich aus der Imperativ Welt kommen dachte ich an einer Art von Lock-Mechanismus aber, als ich bemerkte, wie hässlich ist dies dachte ich über „reine“ Veränderlichkeit, gut, es ist eigentlich ganz rein:
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
Der Punkt ist, dass ein Thread (Akteur) verwaltet eine Liste von Elementen und ändert die Liste nach eingehenden Anforderungen. Da Thread wirklich billig sind, dachte ich, das könnte eine wirklich schöne funktionelle Alternative sein.
Natürlich ist dies nur ein Prototyp (eine schnelle schmutzige Proof of Concept). Also meine Frage ist:
- Ist dies eine „gute“ Art und Weise geteilt änderbare Variablen der Verwaltung (in der Schauspieler Welt)?
- Gibt es bereits eine Bibliothek für dieses Muster? (Ich schon gesucht, aber ich fand nichts)
Grüße, Chris
Wenn Sie bereit sind, Alternativen zum Schauspielermodell zu erkunden, würde ich Ihnen vorschlagen, Haskells [Software Transaktionsspeicher] (https://en.wikipedia.org/wiki/Software_transactional_memory) auszuprobieren. Es ist ein schöner Mechanismus ähnlich zu Datenbanktransaktionen. Siehe [Kapitel 28] (http://book.realworldhaskell.org/read/software-transactional-memory.html) in The Real World Haskell. –
Technisch eine gute Wahl, aber ich habe gehört, dass die Verwendung von STM mit einer großen Anzahl von Threads (ein Thread pro Client, die Standard in Haskell ist) und relativ langen Operationen (Löschen eines Elements aus einer Liste ist O (n), natürlich Hash-Sätze/Karten könnten hier helfen) könnte die Leistung von STM um eine große Anzahl reduzieren. Und natürlich könnte der MVar-Kanal durch den STM-Kanal ersetzt werden, was bedeutet, dass die beste der beiden Techniken verwendet wird. BEARBEITEN: Das Schauspieler-Muster ist in einer solchen Situation im Allgemeinen sehr nett, weil das Löschen/Hinzufügen eines Elements O (1) ist (nur Senden einer Nachricht) Die eigentliche Arbeit ist in einem Thread erledigt ... – Kr0e
Sie haben Recht. Bei STM kann es vorkommen, dass Transaktionen mehrmals neu gestartet werden, was zu einer reduzierten Leistung führt. Wenn Ihre synchronisierten Operationen jedoch lange dauern, können Sie auch zu ähnlichen Problemen mit Akteuren kommen - wenn es mehr Nachrichten gibt, als sie verarbeiten können, wird ihr Zustand hinter der Realität zurückbleiben. Die Verwendung von Balanced Trees ('Map' /' Set') oder 'ST/IO'-basierten Hash-Sets würde definitiv helfen. –