【问题标题】:What is the purpose of the extra result parameter of atomicModifyIORef?atomicModifyIORef 的额外结果参数的目的是什么?
【发布时间】:2016-09-22 13:25:52
【问题描述】:

modifyIORef 的签名很简单:

modifyIORef :: IORef a -> (a -> a) -> IO ()

不幸的是,这不是线程安全的。有一个替代方案可以解决这个问题:

atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b

这两个函数到底有什么区别?在修改可能从另一个线程读取的IORef 时,我应该如何使用b 参数?

【问题讨论】:

    标签: haskell concurrency ioref global-state


    【解决方案1】:

    额外的参数用于提供返回值。例如,您可能希望能够自动替换存储在IORef 中的值并返回旧值。你可以这样做:

    atomicModifyIORef ref (\old -> (new, old))
    

    如果没有要返回的值,可以使用以下方法:

    atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
    atomicModifyIORef_ ref f =
        atomicModifyIORef ref (\val -> (f val, ()))
    

    modifyIORef具有相同的签名。

    【讨论】:

    • 所以,如果是 atomicModifyIORef :: IORef a -> (a -> a) -> IO a,返回旧值,将起到同样的作用(而且更简单,IMO)。很有趣。
    • 我不明白的是,为什么atomicModifyIORef 需要这个功能,而modifyIORef 不需要这个功能?
    • @leftaroundabout 好吧,modifyIORef 无论如何都不提供任何原子性保证,所以它不会那么有用。
    • @chi 这本来会更简单,但当前的解决方案可以做更多的事情。例如,您可以实现某种形式的原子比较和交换,它返回一个布尔标志,指示交换是否发生。
    • 所以关键是,如果我想保留旧值,如果没有并发,我不妨使用old <- readIORef r; writeIORef r $ f old,而在多线程情况下,我必须使用old <- atomicModify r (\x -> (f x, x))确保新值实际上对应于应用于旧值的f,我在old 中得到了一个副本。我理解正确吗?
    【解决方案2】:

    正如您在评论中所说,如果没有并发,您可以编写类似

    modifyAndReturn ref f = do
      old <- readIORef ref
      let !(new, r) = f old
      writeIORef r new
      return r
    

    但在并发上下文中,其他人可以更改读取和写入之间的引用。

    【讨论】:

      【解决方案3】:

      我是这样理解的。想想括号成语后面的函数,例如

      withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
      

      这些函数将函数作为参数并返回该函数的返回值。 atomicModifyIORef 与此类似。它接受一个函数作为参数,目的是返回该函数的返回值。只有一个复杂之处:参数函数还必须返回一个新值以存储在IORef 中。因此,atomicModifyIORef 要求该函数返回两个值。当然,这个案例与括号案例并不完全相似(例如没有涉及IO,我们不处理异常安全等),但是这个类比给了你一个想法。

      【讨论】:

      • 有趣的比较。尽管如此,我现在还是接受dfeuer's answer,因为“为什么atomicModify 需要这个,但modify 需要这个”部分是这个问题的主要内容。
      【解决方案4】:

      我喜欢通过State monad 来查看它。有状态的操作会修改一些内部状态并额外产生一个输出。这里的状态在IORef 内,结果作为IO 操作的一部分返回。因此我们可以使用State 重新构造函数,如下所示:

      import Control.Monad.State
      import Data.IORef
      import Data.Tuple (swap)
      
      -- | Applies a stateful operation to a reference and returns its result.
      atomicModifyIORefState :: IORef s -> State s a -> IO a
      atomicModifyIORefState ref state = atomicModifyIORef ref (swap . runState state)
      

      【讨论】:

        猜你喜欢
        • 2016-01-26
        • 2019-09-08
        • 1970-01-01
        • 1970-01-01
        • 2015-08-24
        • 1970-01-01
        • 2021-07-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多