如何原子地使用一组句柄



假设您有一个包含大量线程的程序。一个线程希望冻结对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提供了一个类似的助手函数,它将一个操作(处理异常)与获取和释放锁括在一起。

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。

最新更新