9

使用相同的管道值执行多个操作是否安全?就像是

do
  let sink = sinkSocket sock

  something $$ sink
  somethingElse $$ sink

我记得在导管的早期版本中,有一些肮脏的黑客行为使其不安全。目前的状态是什么?

(请注意,sinkSocket这不会关闭套接字。)

4

2 回答 2

8

这种用法是完全安全的。旧版本中的问题与模糊可恢复组件和不可恢复组件之间的界限有关。对于现代版本(我认为是从 0.4 开始),两者之间的界限非常清晰。

于 2013-11-05T17:02:53.593 回答
1

从“已使用”接收器的语义不会改变的意义上说,重用接收器可能是安全的。但是您应该意识到另一个威胁:空间泄漏。

这种情况类似于惰性列表:您可以在恒定空间中惰性地消耗一个巨大的列表,但如果您处理该列表两次,它将保存在内存中。递归单子表达式可能会发生同样的事情:如果你使用它一次它的大小是恒定的,但如果你重用它,计算的结构会保存在内存中,从而导致空间泄漏。

这是一个例子:

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没有什么不同,虽然没有数据要保存在内存中,只是单子的结构计算。)

所以,总而言之,我认为意识到这一点很重要。

于 2013-11-13T16:10:11.030 回答