并发(多线程)程序挂起,除非我打印一些东西



我有一个函数,我使用 forkIO 分叉到一个线程中。该函数在 ReaderT monad 转换器中运行,以便我可以传入只读配置记录:

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

问题是程序在运行时挂起。它似乎陷入了一个循环。在main中调用putStr ""无济于事,但是,在watcher内部调用putStr ""确实会触发线程 - 它开始旋转并按预期打印出Thing秒。

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

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

如果有帮助,在将所有东西都包在ReaderT变压器中之前,我没有遇到这个问题。我只是在传递config作为论据。

尽管你的问题中有文字,但我建议你让watcher阻止。确实很少需要在MVar上进行非阻塞操作;通常想要它表明你还没有完全内化"分叉一切"的心态。所以:

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)

相关内容

最新更新