我想把下面的JavaScript代码移植到Haskell:http://jsfiddle.net/mz68R/
这就是我所尝试的:
import Control.Concurrent
import Data.IORef
type EventStream a = IORef [MVar a]
newEventStream :: IO (EventStream a)
newEventStream = newIORef []
setEvent :: EventStream a -> a -> IO ()
setEvent stream event = readIORef stream >>= mapM_ (`putMVar` event)
getEvent :: EventStream a -> (a -> IO b) -> IO ThreadId
getEvent stream listener = do
event <- newEmptyMVar
modifyIORef stream (++ [event])
forkIO $ loop (takeMVar event >>= listener)
loop :: Monad m => m a -> m ()
loop a = a >> loop a
main = do
fib <- newEventStream
getEvent fib $ \(a, b) -> do
print (a, b)
setEvent fib (b, a + b)
setEvent fib (0,1)
它的部分工作原理与预期一致:它不会产生一个无限的斐波那契数列,它会打印出不同数目的斐波那契数列:
aaditmshah@home:~$ runhaskell EventStream.hs
(0,1)
(1,1)
aaditmshah@home:~$ runhaskell EventStream.hs
(0,1)
(1,1)
(1,2)
(2,3)
(3,5)
aaditmshah@home:~$ runhaskell EventStream.hs
(0,1)
(1,1)
(1,2)
(2,3)
(3,5)
(5,8)
(8,13)
(13,21)
(21,34)
(34,55)
(55,89)
(89,144)
(144,233)
(233,377)
(377,610)
(610,987)
(987,1597)
(1597,2584)
(2584,4181)
(4181,6765)
(6765,10946)
我认为这个问题是由于getEvent
函数中的并发引起的,但是我不能确定是什么原因。我如何重构代码来缓解这个问题呢?
2条答案
按热度按时间i1icjdpr1#
当你运行Haskell程序时,它会在主线程退出时立即退出。你会遇到一个竞争条件:
getEvent
的子线程试图在进程退出之前完成尽可能多的工作。一个简单的修复方法是添加一个导入行
import Control.Monad (forever)
,然后在main
的末尾运行:这将导致主线程永远休眠。更好的方法取决于实际应用程序的用途。
flvlnr442#
与Michael的答案不同,您可以使用
async
库,它包含了许多很好的并发模式。它在另一个线程中运行输入
IO
操作,并立即返回一个Async
Package 的返回值。显然,在等待子进程完成足够长的时间之前,我们无法取出a
,但立即返回允许我们在子进程诞生和等待其完成之间做一些事情这里的要点是,您可以使用
async
启动主线程中的所有子线程,然后让它等待所有子线程的返回值,然后才允许终止。在您的例子中,您的子线程永远不会返回,因此这意味着主线程将只是愉快地停止,直到您的程序被中断。