在Haskell中使用MVars实现事件流

bbmckpt7  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(142)

我想把下面的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函数中的并发引起的,但是我不能确定是什么原因。我如何重构代码来缓解这个问题呢?

i1icjdpr

i1icjdpr1#

当你运行Haskell程序时,它会在主线程退出时立即退出。你会遇到一个竞争条件:getEvent的子线程试图在进程退出之前完成尽可能多的工作。
一个简单的修复方法是添加一个导入行import Control.Monad (forever),然后在main的末尾运行:

forever $ threadDelay maxBound

这将导致主线程永远休眠。更好的方法取决于实际应用程序的用途。

flvlnr44

flvlnr442#

与Michael的答案不同,您可以使用async库,它包含了许多很好的并发模式。

async :: IO a -> IO (Async a)

它在另一个线程中运行输入IO操作,并立即返回一个Async Package 的返回值。显然,在等待子进程完成足够长的时间之前,我们无法取出a,但立即返回允许我们在子进程诞生和等待其完成之间做一些事情

-- | "Work".
work :: Int -> IO ()
work n = threadDelay (n * 10000)

do ret <- async $ do work 100 -- do some "work"
                     return True
   putStrLn "Not waiting on the child process yet; doing other work"
   work 5
   putStrLn "Now we wait"
   _ <- wait ret

这里的要点是,您可以使用async启动主线程中的所有子线程,然后让它等待所有子线程的返回值,然后才允许终止。
在您的例子中,您的子线程永远不会返回,因此这意味着主线程将只是愉快地停止,直到您的程序被中断。

相关问题