我正在尝试使用channels / STM在Haskell中实现消息传递。也许这是一个糟糕的想法,并且有一种更好的方法来实现/使用Haskell中的消息传递。如果是这种情况,请告诉我;但是,我的任务已经打开了关于并发Haskell的一些基本问题。
我听说过STM很棒的东西,尤其是Haskell中的实现。既然它支持阅读和写作,并且有一些安全上的好处,我认为可以从那里开始。这引出了我最大的问题:
msg <- atomically $ readTChan chan
其中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
用ghc --make -threaded编译它,然后运行程序,实际上你得到1然后打印到控制台2。现在,假设我们做了
main = do
chan <- atomically $ newTChan
forkIO $ p chan
forkIO $ q chan
代替。现在,如果我们使用 - threaded,它将打印任何内容,1或1后跟2到终端;但是,如果你不用-threaded编译它总是打印1后跟2.问题2:-threaded和not之间有什么区别?我想它们并不是真正的并发运行,它们只是一个接一个地运行。这与以下内容一致。
现在,按照我的想法,如果我同时运行p和q;即我对它们进行分叉,它们应该能够以相反的顺序运行。假设
main = do
chan <- atomically newTChan
forkIO $ q chan
forkIO $ p chan
现在,如果我在没有-threaded的情况下编译它,我从来没有打印到控制台。如果我用-threaded编译,我有时会这样做。虽然,很少得到1然后是2 - 通常只有1或没有。我用Control.Concurrent.Chan尝试了这个,并得到了一致的结果。
第二个大问题:频道和分支如何一起播放,以及上述节目中发生了什么?
无论如何,我似乎无法如此天真地模拟使用STM的消息传递。也许Cloud Haskell可以解决这些问题 - 我真的不知道。有关如何使消息传递缺乏序列化的任何信息~~&gt;写入套接字~~&gt;从插座中读取~~&gt;反序列化将非常感激。
答案 0 :(得分:9)
没有你的想法是正确的 - 这是TChan
的用途 - 你只是错过了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 ())
它按预期工作:
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