Haskell:原子IO包装器/惰性?

4

我写了下面这个函数,我相信它应该能够原子性地执行IO操作(只要其他人也在使用同一个MVar变量)。

atomicIO :: MVar () -> IO a -> IO a
atomicIO mvar io =
  do
    takeMVar mvar
    result <- io
    putMVar mvar ()
    return result

此外,据我了解,Haskell IO 的某些部分是惰性的(例如 IORef),因此在本节中实际执行操作并不必要。它可以返回一个“thunk”(这个词是正确的吗?),其中列出了需要完成的任务。
想法是,关键部分(即单线程部分)应该相当快。
然而,假设我正在写入 IORef,并且我希望 Haskell 立即开始计算结果,以便在需要时准备好使用。但正如我之前所说,当我们持有 MVar 锁定时,我不想被锁定在关键部分。
因此,我考虑了这个方法:
    result <- io `par` io

或者这个

    return result `par` result

或者这个

    result `par` return result

但我不确定这是否能胜任。其中哪种方法是正确的,还是有其他的方法?(我对后两种方法的担忧在于IO()操作,因为我认为并行计算()并没有真正做任何工作)。

2个回答

4

Where you have

writeIORef myRef newValue

将其替换为

newValue `par` writeIORef myRef newValue

这将启动一个线程在后台评估newValue...

...但需要注意的是,它只会将其评估到WHNF(基本上只评估数据结构的第一层)。因此,Int将被完全评估,而String则不会。对于Maybe a值,将完全评估为Nothing或部分评估为Just _

因此,如果您正在使用更复杂的数据结构,则需要使用来自deepseq包中Control.DeepSeq的force,它将完全评估该值。

force newValue `par` writeIORef myRef newValue

如果你想使用 modifyIORef,我认为你不能直接重用 modifyIORef,但你可以定义一个类似的函数modifyIORef 无论如何都是基于 readIORefwriteIORef 定义的)

modifyIORefInParallel :: NFData a => IORef a -> (a -> a) -> IO ()
modifyIORefInParallel ref f
   = do old <- readIORef ref
        let new = f old
        force new `par` writeIORef ref new

注意,如果最后一行是force(f old)`par`writeIORef ref (f old),它不起作用: 强制并行计算的值不是存储在引用中的值。
使用force需要满足NFData a限制。

dave4420: 有没有一种类似于 modifyIORef 的方法,或者我需要先进行单独的 readIORef,然后再进行 writeIORef - Clinton
我猜无论如何,如果我在它周围加锁也不会有太大关系。 - Clinton

1

获得快速临界区的唯一方法是限制自己只使用快速IO操作。我不明白试图在atomicIO内强制执行严格评估如何能够加速。此外,请注意,atomicIO本身可能不会被严格评估,在这种情况下,atomicIO内的严格评估没有任何效果。

无论如何,您可以使用seq而不是par进行严格评估,因为您不尝试激发并行计算。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接