Haskell:Atomic IO包装/懒惰?

时间:2012-04-16 06:12:09

标签: haskell concurrency

我编写了以下函数,我认为应该以原子方式执行IO(只要其他人使用相同的MVar)。

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

另外,根据我的理解,Haskell IO的某些部分是惰性的(例如.. IORef s),因此不需要在本节中实际执行该操作。它可以返回一个'thunk'(是正确的单词吗?),它列出了需要完成的任务。

这个想法是,关键部分(即单线程部分)应该非常快。

但是,假设我正在写IORef,我希望Haskell立即开始计算结果,因此可以在需要时做好准备。但就像我之前说的那样,当我们持有MVar锁时,我不想被锁定在关键部分。

所以我考虑过这样做:

    result <- io `par` io

或者

    return result `par` result

或者

    result `par` return result

但我不确定这是否能胜任。这些是正确的方法之一,还是有另一种方式? (我对后两者的关注是IO ()个动作,因为我认为并行评估()实际上并没有做任何工作。

2 个答案:

答案 0 :(得分:4)

你在哪里

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 is defined in terms of readIORef and writeIORef anyway)

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)则不起作用:并行强制的值不会是ref中存储的值。)

NFData a限制来自使用force。)

答案 1 :(得分:1)

获得快速关键部分的唯一方法是限制自己快速执行IO操作。 我没有看到如何在atomicIO内部强制进行严格评估会让你加快速度。此外,请注意atomicIO本身可能没有严格评估,在这种情况下atomicIO内的严格评估没有任何影响。

在任何情况下,您都可以使用seq代替par进行严格评估,因为您并未试图引发并行计算。