Egymással kommunikáló szálakat csak a számítások szintjén lehet megvalósítani.
Új szál indítása az IO monádban (Control.Concurrent
modulból):
:: IO () → IO ThreadId
Példa:
A szálak közti kommunikációra a standard output nem alkalmas.
A kommunikáció osztott szinkronizált állapoton keresztül lehetséges.
MVar
TVar
MVar
alaponA Control.Concurrent.MVar
modulból:
Két állapot: vagy üres vagy a
típusú érték van benne.
Alapműveletek:
A szál blokkolódik amíg nem sikerül elvégezni a műveleteket.
A többszálú végrehajtás tud legalább annyit, mint a többszálú kiértékelés:
Készítsünk egy függvényt, ami két kifejezést párhuzamosan értékel ki, és az előbb befejeződő kiértékelés végeredményével tér vissza!
merge :: a -> a -> IO a
merge x y = do
mv <- newEmptyMVar
id1 <- forkIO $ x `pseq` putMVar mv x
id2 <- forkIO $ y `pseq` putMVar mv y
v <- takeMVar mv
killThread id1
killThread id2
return v
Teszteset:
Segítség: Figyeljünk arra, hogy az végeredmény megkapása után ne maradjon futó szál!
TVar
alaponSTM: Software transactional memory
Az stm
csomagban levő Control.Concurrent.STM
modult használjuk.
Az STM
hasonlít az IO
-hoz, de a számításai atomiak:
Atomi számítás végrehajtása:
A TVar
műveletei hasonlóak az IOVar
műveleteihez:
Előny: miközben egymás után írjuk és olvassuk a változókat, más biztos hogy nem nyúl hozzájuk.
transfer :: TVar Int -> TVar Int -> Int -> IO ()
transfer from to amount =
atomically $ do
balance <- readTVar from
if balance < amount
then retry
else do
writeTVar from (balance - amount)
tobalance <- readTVar to
writeTVar to (tobalance + amount)
Számítás újrajátszása:
orElse
transfer
addig próbálkozik, amíg le nem emelhető az összeg (csak változás esetén próbálja újra). Hogy tudunk mást csinálni, hogy ha nem sikerült?
orElse
típusa és néhány törvény: