如何原子地使用一组句柄?

时间:2015-03-31 21:15:06

标签: multithreading haskell stdin handle atomicity

假设您有一个包含大量线程的程序。一个线程想要冻结对stdin,stdout和stderr的访问(导致任何其他线程或键盘阻塞直到完成),以便它的输出不会与它们交织。有没有办法直接这样做,或者必须有一个经理线程,你知道,管理。相关地,你能否导致stdin上的任何输入阻止stdout上的任何输出,直到它被接收和处理(原子地)?

1 个答案:

答案 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一个操作(处理异常),获取并释放锁。

Lockasync而言,上述示例可以简化为

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。