Haskell atomicModifyIORef的额外结果参数的用途是什么?
Haskell atomicModifyIORef的额外结果参数的用途是什么?,haskell,concurrency,ioref,global-state,Haskell,Concurrency,Ioref,Global State,modifyIORef的签名非常简单: modifyIORef :: IORef a -> (a -> a) -> IO () 不幸的是,这不是线程安全的。有一种方法可以解决这个问题: atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b 这两种功能之间到底有什么区别?当修改可能从另一个线程读取的IORef时,如何使用b参数?额外的参数用于提供返回值。例如,您可能希望能够自动替换存储在IORef中的值并返
modifyIORef
的签名非常简单:
modifyIORef :: IORef a -> (a -> a) -> IO ()
不幸的是,这不是线程安全的。有一种方法可以解决这个问题:
atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b
这两种功能之间到底有什么区别?当修改可能从另一个线程读取的
IORef
时,如何使用b
参数?额外的参数用于提供返回值。例如,您可能希望能够自动替换存储在IORef
中的值并返回旧值。您可以这样做:
atomicModifyIORef ref (\old -> (new, old))
如果没有要返回的值,可以使用以下选项:
atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ ref f =
atomicModifyIORef ref (\val -> (f val, ()))
它的签名与
modifyIORef
相同,我是这样理解的。想想括号习惯用法后面的函数,例如
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
这些函数将函数作为参数并返回该函数的返回值<代码>原子修改ioref与此类似。它将函数作为参数,目的是返回该函数的返回值。这里只有一个复杂问题:argument函数还必须返回一个新值以存储在
IORef
中。因此,atomicModifyIORef
需要从该函数返回两个值。当然,这种情况与括号中的情况并不完全相似(例如,不涉及IO
,我们不涉及异常安全等),但这种类比给了您一个想法。如您在评论中所述,如果没有并发性,您就可以编写如下内容
modifyAndReturn ref f = do
old <- readIORef ref
let !(new, r) = f old
writeIORef r new
return r
modifyAndReturn ref f=do
old我喜欢通过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)
因此,如果它是atomicModifyIORef::IORef a->(a->a)->IO a
,返回旧值,则会达到相同的目的(而且更简单,IMO)。有趣的是。我不明白的是,为什么我需要这个功能用于atomicModifyIORef
,而不是modifyIORef
?@leftarounda好吧,modifyIORef
无论如何都不提供任何原子性保证,所以它对它没有那么大的用处。@chi那会更简单,但目前的解决方案可以做更多的事情。例如,您可以实现某种形式的原子比较和交换,它返回一个布尔标志,指示交换是否发生。因此,如果没有并发性,我最好使用旧的有趣的比较。尽管如此,我现在还是会接受,因为“为什么atomicModify
需要这样做,但是modify
需要这样做”部分就是这个问题的主要内容。