强制评估IORef:rnf,deepSeq或其他什么?

时间:2011-07-12 10:11:21

标签: multithreading haskell ioref

我有一个长期运行的过程,forkIO'd,它产生像素颜色值:

takesAgesToRun :: [[Color]]

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         writeIORef ref colors

(其中Color只包含三个Double值。

正如预期的那样,当在IORef的“另一侧”读取时,存储的值只是一个thunk,因此会阻止主进程。

我知道我需要完全评估[[Color]]值以达到正常形式,但似乎有两种方法可以实现这一点,而且,我不确定如何将其合并到我的代码中。< / p>

我该如何解决这个问题?我是否使用rnfdeepSeq或其他线程策略?其中一个是首选,其他人是否已被弃用?它如何适合我的代码?

(PS请忽略将图像存储为颜色列表列表的事实是愚蠢的 - 这只是代码的简化版本。)

1 个答案:

答案 0 :(得分:5)

使用deepSeq。它就像seq一样使用。你可以这样加入它:

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         deepSeq colors $ writeIORef ref colors

这将强制在“writeIORef”调用之前完全评估“colors”。

为了实现此目的,您需要NFData Color个实例。究竟如何写这取决于Color的定义,但这里有两个例子:

-- just for reference
data Color = Color Double Double Double

instance NFData Color where
    rnf (Color r g b) = r `seq` g `seq` b `seq` ()

-- closer to the likely actual implementation for Color
data Color2 = Color2 !Double !Double !Double

instance NFData  Color2 where
-- the default implementation is fine

对于Color实例,您需要确保只要Color为颜色的所有组件都会被完全评估[1]。这就是seq所做的。我们可以在此处使用seq而不是deepSeq,因为我们知道每个组件都是Double,因此由seq完全评估。如果组件是更复杂的数据类型,那么在编写NFData实例时我们需要使用deepSeq

Color2中,它有点简单。由于爆炸模式,我们知道在Color2时对组件进行了全面评估。这意味着我们可以使用默认实现,它将Color2评估为弱头正常形式,由于完全评估了爆炸模式。

rnf与Control.Parallel.Strategies结合使用时非常有用。以下是deepSeq

的当前定义
deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b

所有deepseq都会调用rnf并保证对其output()进行求值。这实际上是直接使用rnf的唯一方法。

[1] Haskell只提供两种评估内容的通用方法:模式匹配和seq。其他一切都建立在其中一个或两个上。对于NFData Color实例,首先通过与Color构造函数的模式匹配将Color计算为WHNF,然后通过seq评估组件。

当然还有第三种高度专业化的评估方法:即执行函数main :: IO ()以评估()