dam*_*iya 31 parallel-processing haskell immutability lazy-evaluation
人们认为Haskell在并行性方面具有优势,因为它具有不变的数据结构。但是Haskell也很懒。这意味着实际上可以将数据从thunk突变为评估结果。
因此,懒惰似乎会损害不变性的优势。我是错的还是Haskell有针对此问题的对策?还是这是Haskell的特色?
Jon*_*rdy 32
是的,GHC的RTS使用thunk来执行非严格评估,并且它们在引擎盖下使用了变异,因此需要进行一些同步。但是,由于大多数堆对象是不可变的并且函数是引用透明的,因此简化了此过程。
在多线程程序中,对thunk的评估如下:
重击在原子上被†替换为BLACKHOLE对象
如果相同的线程在将thunk更新为a之后试图强制thunk,则BLACKHOLE表示死循环,并且RTS抛出异常(<<loop>>)
如果不同的线程试图在a时强制执行thunk,则BLACKHOLE它将阻塞,直到原始线程完成对thunk的求值并用值更新它为止
评估完成后,原始线程原子地将†替换为thunk及其结果
†例如,使用比较和交换(CAS)指令
因此这里有一个潜在的竞争:如果两个线程试图同时强制相同的重击,则它们都可能开始评估它。在这种情况下,它们将做一些多余的工作-但是,一个线程将成功覆盖BLACKHOLE结果,而另一个线程将简单地丢弃它计算出的结果,因为它的CAS将失败。
安全代码无法检测到这一点,因为它无法获取对象的地址或确定thunk的状态。实际上,这种冲突很少见,原因有以下几点:
并发代码通常以适合特定问题的方式跨线程划分工作负载,因此重叠的风险很小
在达到弱头正常形态之前,对重击的评估通常是“浅”的,因此“碰撞”的可能性较低
因此,当实施非严格评估时,即使在并发情况下,重击最终也提供了良好的性能折衷。