在Parallel and Concurrent Haskell的MVar记录示例中,它显示以下内容:
import Control.Concurrent
import Control.Concurrent.MVar
data Logger = Logger (MVar LogCommand)
data LogCommand = Message String | Stop (MVar ())
initLogger :: IO Logger
initLogger = do
m <- newEmptyMVar
let l = Logger m
forkIO (logger l)
return l
logger :: Logger -> IO ()
logger (Logger m) = loop
where
loop = do
cmd <- takeMVar m
case cmd of
Message msg -> putStrLn msg >> loop
Stop s -> do
putStrLn "logger: stop"
putMVar s ()
logMessage :: Logger -> String -> IO ()
logMessage (Logger m) s = putMVar m (Message s)
logStop :: Logger -> IO ()
logStop (Logger m) = do
s <- newEmptyMVar
putMVar m (Stop s)
takeMVar s
如果logger
的最后一行,即putMVar s ()
被删除,会丢失什么?
答案 0 :(得分:4)
如果没有putMVar s ()
,调用logStop
会在takeMVar s
中挂起,因为其他任何内容都无法填充s
。最终会抛出BlockedIndefinitelyOnMVar
异常,杀死挂起的线程。
s
存在有序关闭。它保证在logStop
返回时,日志处理线程实际上已停止。
请参阅您链接的页面中的此文字:
[...] {...}}导致日志记录服务终止。后一个操作很重要,因为如果我们要关闭程序,我们需要确保日志服务已经完成处理任何未完成的请求。回想一下“A Simple Example: Reminders”当主线程退出时,程序立即终止,而不是等待其他线程先终止。因此
logStop
有一个额外的要求:在日志记录服务处理完所有未完成的请求并停止之前,它不得返回。
[...]
发送命令后,我们在新
logStop
上调用takeMVar
以等待响应。在日志记录线程处理了MVar
命令后,它将Stop
放入此()
,这允许MVar
继续并takeMVar
返回。