除非我打印某些内容,否则并发(多线程)程序会挂起

时间:2019-06-24 10:22:37

标签: multithreading haskell concurrency

我有一个函数,可以使用forkIO派生到线程中。该函数在ReaderT monad transformer中运行,以便我可以传递只读配置记录:

main :: IO ()
main = do 
    ...
    forkIO $ runReaderT watcher config

watcher函数使用tryTakeMVar监视MVar(我不想阻止它。)MVar存储在配置中,并称为“抽屉”,因为它的行为类似于事务抽屉在mainwatcher正在监视的线程之间切换,基本上是一个跳过频道。

printThing的签名为printThing :: Thing -> ReaderT Config IO (),并调用putStrLn打印Thing

watcher :: ReaderT Config IO ()                                                                                                                                                                       
 watcher = do
     cfg <- ask
     mNewThing <- liftIO $ tryTakeMVar $ drawer cfg
     case mNewThing of
         Nothing -> do
            --liftIO $ putStr ""  -- uncommenting this helps
            watcher
         Just newThing -> do
             printThing newThing
             watcher

问题是程序在运行时挂起。它似乎陷入了循环。在putStr ""中调用main并没有帮助,但是,在putStr ""内部调用watcher会触发线程-它开始旋转并打印出Thing预期的。

我能想出的是,我被懒惰所咬,但是我不确定在哪里。我尝试过在可能的情况下使用$!

我在watcher的某些条件下执行IO操作,但不是全部。那是问题吗?我需要在所有条件分支中执行IO操作吗?

如果有帮助,在将所有内容包装在ReaderT转换器中之前,我没有这个问题。我只是在传递config作为参数。

1 个答案:

答案 0 :(得分:3)

尽管您的问题中有文字,我还是建议您让watcher阻止。确实很少需要在MVar上执行非阻塞操作。通常希望它是sign you haven't quite internalized the "fork everything" mentality。所以:

watcher :: ReaderT Config IO ()
watcher = do
    cfg <- ask
    newThing <- liftIO . takeMVar $ drawer cfg
    printThing newThing
    watcher

我们可以单独解决一个问题,形式为“如何实现效果X,在我看来,这需要非阻塞操作,而仅使用阻塞操作?”如果您要编写一个单独的问题,其中包含有关效果X的一些详细信息。

旁注:我很想用以下方式写上面的内容,虽然含义相同,但从美学角度来说更吸引我:

watcher = forever (asks drawer >>= liftIO . takeMVar >>= printThing)