atomicModifyIORef 如何导致泄漏?为什么atomicModifyIORef'可以解决这个问题?

问题描述 投票:0回答:1

如果我在

Hoogle
上搜索 IORef a -> (a -> (a, b)) -> IO b,第一个结果是

atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b

基础Data.IORef

原子地修改

IORef
的内容。

此函数对于在多线程程序中安全地使用

IORef
非常有用。如果您只有一个
IORef
,那么使用
atomicModifyIORef
访问和修改它将防止竞争条件。

将原子性扩展到多个

IORefs
是有问题的,因此建议如果您需要做更复杂的事情,那么使用
MVar
是一个好主意。

atomicModifyIORef
不严格应用该功能。即使您所做的只是替换该值,了解这一点也很重要。例如,这会泄漏内存:

ref <- newIORef '1'
forever $ atomicModifyIORef ref (\_ -> ('2', ()))

使用

atomicModifyIORef'
atomicWriteIORef
可以避免此问题。

该函数施加了内存屏障,防止重新排序;有关详细信息,请参阅 Data.IORef#memmodel。

(我不确定为什么,如果我单击这三个链接中的任何一个,生成的文档页面似乎不包含文本将泄漏内存,该文本包含在上面的摘录中。)

问题有两个方面:

  • 为什么上面的例子会泄漏内存?
  • 为什么用
    atomicModifyIORef'
    代替
    atomicModifyIORef
    就不会泄漏?
haskell memory-leaks functional-programming atomic ioref
1个回答
0
投票

让我们运行代码。

ref <- newIORef '1'

在这行之后,IORef 的内容就是

'1'

让我们应用一次此操作:

atomicModifyIORef ref (\_ -> ('2', ()))

此行之后,IORef 的内容为

(\_ -> '2') '1'
。 请注意,由于懒惰,这not简化为
'2'
。但被保留为未评估的重击。 (
atomicModifyIORef'
会简化这一点)。

再次应用此操作:

atomicModifyIORef ref (\_ -> ('2', ()))

现在 IORef 的内容是

(\_ -> '2') ((\_ -> '2') '1')

等等,看到图案了吗?当我们可以(并且应该)简化其内容时,我们构建了越来越大的未评估的重击,浪费了内存。

© www.soinside.com 2019 - 2024. All rights reserved.