假设您有一个包含大量线程的程序。一个线程希望冻结对stdin、stdout和stderr的访问(导致任何其他线程或键盘阻塞,直到它完成为止),这样它的输出就不会与它们交织在一起。有没有办法直接做到这一点,或者必须有一个管理器线程,你知道,操纵手柄。与此相关的是,您是否可以使stdin上的任何输入阻塞stdout上的任何输出,直到它被接收和处理(原子)?
您可以使用MVar
轻松模拟用于控制对资源访问的锁。通过使用takeMVar
获取值来获取锁,并通过将值替换为putMVar
来释放锁。例如,我们可以定义如下
import Control.Concurrent
import Control.Concurrent.MVar
main = do
stdinLock <- newMVar () -- create a new lock for stdin (unaquired)
let
printWithLabel a b = do
takeMVar stdinLock -- aquire the lock for stdin
putStrLn (show a ++ ":")
print b
putMVar stdinLock () -- release the lock for stdin
actions = map fork $ zipWith printWithLabel [1..26] ['A'..]
doneSignals <- sequence actions
sequence doneSignals
return ()
fork :: IO a -> IO (IO ())
fork a = do
done <- newEmptyMVar
forkIO (a >> putMVar done ())
return (takeMVar done)
我们可以将锁定功能提取到另一个功能中
withLock :: MVar () -> IO a -> IO a
withLock lock action = do
takeMVar lock
x <- action
putMVar lock ()
return x
CCD_ 4在获取锁之后执行CCD_。如果代码抛出异常,这将无法正确处理该怎么办,尤其是在抛出异常时不会释放锁。concurrent extra中的Lock
提供了一个类似的助手函数,它将一个操作(处理异常)与获取和释放锁括在一起。
就Lock
和async
而言,上述示例可以简化为
import qualified Control.Concurrent.Lock as Lock
import Control.Concurrent.Async
main = do
stdinLock <- Lock.new
let
printWithLabel a b = Lock.with stdinLock $ do
putStrLn (show a ++ ":")
print b
actions = zipWith printWithLabel [1..26] ['A'..]
doneSignals <- mapM async actions
mapM_ wait doneSignals
如果您希望读取stdin上输入的线程阻止其他线程向stdout的输出,则可以使用单个锁来控制stdin和stdout。