2015-05-20 25 views
5

Próbuję użyć kanałów/STM do implementacji przekazywania komunikatów w Haskell. Może to okropny pomysł i istnieje lepszy sposób na wdrożenie/wykorzystanie przekazywania wiadomości w Haskell. Jeśli tak, daj mi znać; jednak moje zadanie otworzyło kilka podstawowych pytań na temat współbieżnego Haskella.Haskell, kanały, STM, -threaded, Message Passing

Słyszałem wspaniałych rzeczy o STM, aw szczególności wykonanie w Haskell. Ponieważ wspiera czytanie i pisanie oraz ma pewne zalety związane z bezpieczeństwem, pomyślałem, że zacznie się tam. Podnosi to mój największy pytanie: czy

msg <- atomically $ readTChan chan 

gdzie chan jest TChan Int, spowodować oczekiwanie, że czeka na kanał, aby mieć wartość na nim?

Rozważmy następujący program:

p chan = do 
    atomically $ writeTChan chan 1 
    atomically $ writeTChan chan 2 

q chan = do 
    msg1 <- atomically $ readTChan chan 
    msg2 <- atomically $ readTChan chan 
    -- for testing purposes 
    putStrLn $ show msg1 
    putStrLn $ show msg2 

main = do 
    chan <- atomically $ newTChan 
    p chan 
    q chan 

skompilować ten z ghc --make -threaded, a następnie uruchomić program, i rzeczywiście można się 1, a następnie przez 2 drukowane na konsoli. Teraz załóżmy, że zamiast tego używamy

main = do 
    chan <- atomically $ newTChan 
    forkIO $ p chan 
    forkIO $ q chan 

. Teraz, jeśli użyjemy - threaded, to albo nic nie wydrukuje, 1, albo 1, a następnie 2 na terminal; jednak, jeśli nie skompilujesz się z -threaded, zawsze wypisze 1, a następnie 2. Pytanie 2: jaka jest różnica między -threaded a nie? Wyobrażam sobie, że tak naprawdę nie działają jako obiekty współbieżne, a one są po prostu uruchamiane jedna po drugiej. Jest to zgodne z poniższym opisem.

Teraz, w moim myśleniu gdybym miał piq działać równolegle; tj. je rozwinąłem, powinny być w stanie działać w odwrotnej kolejności. Załóżmy, że

Teraz, jeśli skompiluję to bez-threaded, nigdy nie dostanę niczego wydrukowanego do konsoli. Jeśli kompiluję z -threaded, czasami robię. Chociaż bardzo rzadko zdarza się uzyskać 1, a następnie 2 - zwykle tylko 1 lub nic. Próbowałem również z Control.Concurrent.Chan i uzyskałem spójne wyniki.

Drugie ważne pytanie: w jaki sposób kanały i widelec bawią się razem, i to, co dzieje się w powyższym programie?

W każdym razie wydaje się, że nie potrafię tak naiwnie symulować przekazywania wiadomości przez STM. Być może Cloud Haskell jest opcją, która rozwiązuje te problemy - naprawdę nie wiem. Wszelkie informacje o tym, jak uzyskać przekazywanie komunikatów, które nie wymagają serializowania ~~>, zapisują do gniazda ~~> odczytane z socket ~~> deserialize, byłyby bardzo doceniane.

+0

Re: „Jaka jest różnica między -threaded a nie”, który może Cię zainteresować [ moja ekspozycja na temat wątków Haskella] (http://dmwit.com/gtk2hs). Zignoruj ​​bity specyficzne dla gtk. –

Odpowiedz

8

Nie twój pomysł ma rację - to kindof co TChan s służą - po prostu brakowało niewielki punkt forkIO:

Problemem jest to, że główny wątek nie będzie czekać na zakończenie wątków utworzonych z forkIO (see here for reference)

więc jeśli mogę użyć podpowiedź podany w nawiązaniu: Prace

import Control.Concurrent 
import Control.Concurrent.STM 

p :: Num a => TChan a -> IO() 
p chan = do 
    atomically $ writeTChan chan 1 
    atomically $ writeTChan chan 2 

q chan = do 
    msg1 <- atomically $ readTChan chan 
    msg2 <- atomically $ readTChan chan 
    -- for testing purposes 
    putStrLn $ show msg1 
    putStrLn $ show msg2 

main :: IO() 
main = do 
    children <- newMVar [] 
    chan <- atomically $ newTChan 
    _ <- forkChild children $ p chan 
    _ <- forkChild children $ q chan 
    waitForChildren children 
    return() 

waitForChildren :: MVar [MVar()] -> IO() 
waitForChildren children = do 
    cs <- takeMVar children 
    case cs of 
    [] -> return() 
    m:ms -> do 
     putMVar children ms 
     takeMVar m 
     waitForChildren children 

forkChild :: MVar [MVar()] -> IO() -> IO ThreadId 
forkChild children io = do 
    mvar <- newEmptyMVar 
    childs <- takeMVar children 
    putMVar children (mvar:childs) 
    forkFinally io (\_ -> putMVar mvar()) 

to s zgodnie z oczekiwaniami:

d:/Temp $ ghc --make -threaded tchan.hs 
[1 of 1] Compiling Main    (tchan.hs, tchan.o) 
Linking tchan.exe ... 
d:/Temp $ ./tchan.exe 
1 
2 
d:/Temp $ 

i oczywiście będzie nadal działać, jeśli przełączanie połączeń na p i q zbyt

+1

Czy istnieje moduł/biblioteka, które mogą uprościć to 'forkChild' /' waitForChildren'? – Bergi