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

stu*_*ith 4 multithreading haskell ioref

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

takesAgesToRun :: [[Color]]

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         writeIORef ref colors
Run Code Online (Sandbox Code Playgroud)

(其中Color只包含三个Double值).

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

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

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

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

Joh*_*n L 5

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

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

这将强制在"writeIORef"调用之前完全评估"颜色".

为了使其工作,您将需要一个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
Run Code Online (Sandbox Code Playgroud)

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

Color2它的简单一点.由于爆炸模式,我们知道组件在何时被完全评估Color2.这意味着我们可以使用默认实现,该实现评估Color2为弱头正常形式,由于完全评估了爆炸模式.

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

deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b
Run Code Online (Sandbox Code Playgroud)

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

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

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