假设您有一个包含大量线程的程序。一个线程想要冻结对stdin,stdout和stderr的访问(导致任何其他线程或键盘阻塞直到完成),以便它的输出不会与它们交织。有没有办法直接这样做,或者必须有一个经理线程,你知道,管理。相关地,你能否导致stdin上的任何输入阻止stdout上的任何输出,直到它被接收和处理(原子地)?
答案 0 :(得分:3)
您可以使用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
withLock
获取锁后执行IO
操作,并在完成后释放。如果代码抛出异常,这并没有正确处理该怎么做,如果抛出异常,特别是不会释放锁。 Lock
中的concurrent-extra提供了一个类似的辅助函数,brackets一个操作(处理异常),获取并释放锁。
就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。