4

我有一个长时间运行的进程forkIO,它会产生像素颜色值:

takesAgesToRun :: [[Color]]

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

(其中Color仅包含三个Double值)。

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

我知道我需要完全评估[[Color]]头部正常形式的值,但似乎有两种方法可以实现这一点,此外,我不确定如何将它们合并到我的代码中。

我该怎么办?我是否使用rnf,deepSeq或其他一些线程策略?其中一个是首选的,而其他的是否已弃用?它如何适合我的代码?

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

4

1 回答 1

5

使用deepSeq. 它就像seq. 你会像这样合并它:

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

这将强制在“writeIORef”调用之前对“颜色”进行全面评估。

为了使其工作,您将需要NFData一个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,您需要确保颜色的所有组件都被完全评估[1],只要 Color 是。这就是seqs所做的。我们可以在这里使用seq代替,deepSeq因为我们知道每个组件都是 a Double,因此由 seq 完全评估。deepSeq如果组件是更复杂的数据类型,那么我们在编写 NFData 实例时需要使用。

Color2它有点简单。由于 bang 模式,我们知道组件在何时被完全评估Color2。这意味着我们可以使用默认实现,它评估Color2为弱头部正常形式,由于 bang 模式被完全评估。

rnf主要在与 Control.Parallel.Strategies 结合使用时有用。这是当前的定义deepSeq

deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b

deepseq 所做的只是调用rnf并保证其输出 () 被评估。这确实是rnf直接使用的唯一方法。

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

当然,还有第三种高度专业化的方法来评估东西: main :: IO ()即将执行一个函数来评估().

于 2011-07-12T11:46:02.200 回答