使用相同的管道值执行多个操作是否安全?就像是
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
没有什么不同,虽然没有数据要保存在内存中,只是单子的结构计算。)
所以,总而言之,我认为意识到这一点很重要。