我有一个 导管 管道处理一个长文件。我想每1000条记录为用户打印一份进度报告,所以我写了这样的:
-- | Every n records, perform the IO action.
-- Used for progress reports to the user.
progress :: (MonadIO m) => Int -> (Int -> i -> IO ()) -> Conduit i m i
progress n act = skipN n 1
where
skipN c t = do
mv <- await
case mv of
Nothing -> return ()
Just v ->
if c <= 1
then do
liftIO $ act t v
yield v
skipN n (succ t)
else do
yield v
skipN (pred c) (succ t)
无论我怎么称呼它,它都会泄漏内存,即使我只是告诉它打印一个句号。
据我所知,该函数是尾递归的,并且两个计数器都经常被强制(我尝试将“seq c”和“seq t”放入,但无济于事)。任何线索?
如果我输入一个“awaitForever”,为每条记录打印一份报告,那么它可以正常工作。
更新1:仅在使用-O2编译时才会发生这种情况。分析表明泄漏的内存在递归的“skipN”函数中分配,并由“SYSTEM”保留(无论这意味着什么)。
更新2:我已经成功治愈了它,至少在我目前的计划中是这样。我用这个替换了上面的函数。请注意,“proc”的类型为“Int - > Int - > Maybe i - > m()”:要使用它,请调用“await”并将结果传递给它。出于某种原因,交换“await”和“yield”解决了这个问题。所以现在它在产生前一个结果之前等待下一个输入。
-- | Every n records, perform the monadic action.
-- Used for progress reports to the user.
progress :: (MonadIO m) => Int -> (Int -> i -> IO ()) -> Conduit i m i
progress n act = await >>= proc 1 n
where
proc c t = seq c $ seq t $ maybe (return ()) $ \v ->
if c <= 1
then {-# SCC "progress.then" #-} do
liftIO $ act t v
v1 <- await
yield v
proc n (succ t) v1
else {-# SCC "progress.else" #-} do
v1 <- await
yield v
proc (pred c) (succ t) v1
因此,如果您在Conduit中有内存泄漏,请尝试交换yield并等待操作。