【发布时间】:2013-11-05 16:25:12
【问题描述】:
使用相同的管道值执行多个操作是否安全?类似的东西
do
let sink = sinkSocket sock
something $$ sink
somethingElse $$ sink
我记得在导管的早期版本中,有一些肮脏的黑客行为使其不安全。目前情况如何?
(注意sinkSocket 不会关闭套接字。)
【问题讨论】:
使用相同的管道值执行多个操作是否安全?类似的东西
do
let sink = sinkSocket sock
something $$ sink
somethingElse $$ sink
我记得在导管的早期版本中,有一些肮脏的黑客行为使其不安全。目前情况如何?
(注意sinkSocket 不会关闭套接字。)
【问题讨论】:
这种用法是完全安全的。旧版本中的问题与模糊可恢复组件和不可恢复组件之间的界限有关。对于现代版本(我认为从 0.4 开始),两者之间的界限非常清晰。
【讨论】:
在“已使用”接收器的语义不会改变的意义上,重用接收器可能是安全的。但您应该注意另一个威胁:空间泄漏。
这种情况类似于惰性列表:您可以在恒定空间中惰性地消耗一个巨大的列表,但如果您处理该列表两次,它将保存在内存中。递归单子表达式也可能发生同样的事情:如果你使用它一次它的大小是恒定的,但如果你重复使用它,计算的结构会保存在内存中,导致空间泄漏。
这是一个例子:
import Data.Conduit
import Data.Conduit.List
import Control.Monad.Trans.Class (lift)
consumeN 0 _ = return ()
consumeN n m = do
await >>= (lift . m)
consumeN (n-1) m
main = do
let sink = consumeN 1000000 (\i -> putStrLn ("Got one: " ++ show i))
sourceList [1..9000000::Int] $$ sink
sourceList [1..22000000::Int] $$ sink
这个程序在我的机器上使用了大约 150M 的内存,但是如果你删除最后一行或者在两个地方重复 sink 的定义,你会得到一个很好的恒定空间使用。
我同意这是一个人为的例子(这是我想到的第一个例子),大多数 Sinks 不太可能发生这种情况。例如,您的sinkSocket 不会发生这种情况。 (为什么这是人为的:因为接收器的 控制结构 不依赖于它获得的值。这也是它可能泄漏的原因。)但是,例如,对于源,这将是更常见。 (许多常见的源都表现出这种行为。sourceList 是一个明显的例子,因为它实际上会将源列表保存在内存中。但是,enumFromTo 没有什么不同,尽管没有数据要保存在内存中,只是一元计算的结构。)
所以,总而言之,我认为意识到这一点很重要。
【讨论】: