我有一个长期运行的过程,forkIO
'd,它产生像素颜色值:
takesAgesToRun :: [[Color]]
myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
writeIORef ref colors
(其中Color
只包含三个Double
值。
正如预期的那样,当在IORef
的“另一侧”读取时,存储的值只是一个thunk,因此会阻止主进程。
我知道我需要完全评估[[Color]]
值以达到正常形式,但似乎有两种方法可以实现这一点,而且,我不确定如何将其合并到我的代码中。< / p>
我该如何解决这个问题?我是否使用rnf
,deepSeq
或其他线程策略?其中一个是首选,其他人是否已被弃用?它如何适合我的代码?
(PS请忽略将图像存储为颜色列表列表的事实是愚蠢的 - 这只是代码的简化版本。)
答案 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 ()
以评估()
。