2015-05-20 2 views
5

Я пытаюсь использовать каналы/STM для реализации передачи сообщений в Haskell. Возможно, это ужасная идея, и есть лучший способ реализовать/использовать передачу сообщений в Haskell. Если это так, дайте мне знать; Тем не менее, мои поиски открыли некоторые основные вопросы о параллельном Haskell.Haskell, Channels, STM, -threaded, Message Passing

Я слышал о вещах STM, в частности о реализации в Haskell. Поскольку он поддерживает чтение и запись, и имеет некоторые преимущества в плане безопасности, я решил, что он начнется там. Это вызывает мой самый большой вопрос: действительно

msg <- atomically $ readTChan chan 

где чан является TChan Int, вызывает ожидание, что ждет канала, чтобы иметь значение на нем?

Рассмотрим следующую программу:

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 

Compile это с GHC --make -threaded, а затем запустить программу, и на самом деле вы получаете 1, затем 2 напечатаны на консоли. Предположим, что мы делаем

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

вместо этого. Теперь, если мы будем использовать - threaded, он либо ничего не напечатает, 1 или 1, а 2 - к терминалу; однако, если вы не компилируете с помощью -threaded, он всегда печатает 1, а затем 2. Вопрос 2: в чем разница между -тобой и нет? Я предполагаю, что они на самом деле не работают как параллельные вещи, и они просто запускаются один за другим. Это соответствует следующему.

Теперь, когда я думал, что у меня были p и q, работающие одновременно; т. е. я их сделал, они должны иметь возможность работать в обратном порядке. Предположим, что

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

Теперь, если я скомпилирую это без искажений, я никогда не получаю ничего напечатанного на консоли. Если я скомпилирую с помощью -threaded, я иногда это делаю. Хотя, очень редко можно получить 1, а затем 2 - обычно 1 или ничего. Я попробовал это и с Control.Concurrent.Chan, и получил согласованные результаты.

Второй большой вопрос: как каналы и вилка играют вместе, и что происходит в вышеуказанной программе?

Во всяком случае, кажется, что я не могу так наивно моделировать передачу сообщений с помощью STM. Возможно, Cloud Haskell - это вариант, который решает эти проблемы - я действительно не знаю. Любая информация о том, как получить передачу сообщения, нежели сериализовать ~~> записать в сокет ~~>, читается из сокета ~~> десериализации, была бы чрезвычайно оценена.

+0

Re: «какая разница между -threaded и не», вы могли бы [ моя экспозиция на поточной модели Haskell] (http://dmwit.com/gtk2hs). Игнорировать специфичные для gtk биты. –

ответ

8

Нет Ваша идея прав - это kindof, что TChan s являются для - вы просто пропустили небольшую точку forkIO:

Проблема заключается в том, что ваш основной поток не будет ждать окончания нитей, созданных с forkIO (see here for reference)

так, если я использую подсказку в ссылке:

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()) 

это работа s, как ожидалось:

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 $ 

и, конечно, он будет продолжать работать, если переключать вызовы p и q тоже

+1

Есть ли какой-либо модуль/библиотека, который может упростить эту вещь 'forkChild' /' waitForChildren'? – Bergi

Смежные вопросы