【问题标题】:How does one atomically use a set of handles?如何以原子方式使用一组句柄?
【发布时间】:2015-03-31 21:15:06
【问题描述】:

假设您有一个包含一堆线程的程序。一个线程想要冻结对 stdin、stdout 和 stderr 的访问(导致任何其他线程或键盘阻塞直到完成),这样它的输出就不会与它们交织在一起。有没有办法直接做到这一点,或者必须有一个管理器线程,你知道,管理句柄。与此相关的是,您是否可以让 stdin 上的任何输入阻塞 stdout 上的任何输出,直到它(原子地)接收和处理?

【问题讨论】:

  • 我模糊地记得有人声称stdout已经 阻止了单行中的文本交错。虽然我找不到权威的参考...
  • @MathematicalOrchid 如果您在我的任何一个示例中将putStLn 更改为putStr,您将在一行中看到交错的文本。
  • 那显然我错了……

标签: multithreading haskell stdin handle atomic


【解决方案1】:

您可以使用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 操作,并在完成后释放它。如果代码抛出异常,这不能正确处理该怎么做,尤其是如果抛出异常则不会释放锁。 concurrent-extra 中的 Lock 提供了一个类似的帮助函数,它 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。

【讨论】:

  • 我希望能够使用现有代码进行这项工作。锁需要一切来支持它们。
  • @PyRulez 如果您不以某种方式标记线程所在位置的边缘并且不允许交错,则有两种可能性:允许线程在任何地方交错(无锁)或不允许线程在任何地方交错(串行执行)。如果您从上一个示例中删除 Lock.with stdinLock $,则所有现有的写入 stdout 的底层调用都不会相互交错,但输出数据仍然是无意义的(所有标签首先紧跟所有值)。跨度>
猜你喜欢
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多