【问题标题】:Why is streaming-bytestring giving me error "openBinaryFile: resource exhausted (Too many open files)"?为什么流式字节串给我错误“openBinaryFile:资源耗尽(打开的文件太多)”?
【发布时间】:2019-05-12 20:09:49
【问题描述】:

streaming-bytestring 库在打印大约 512 个字节后出现错误。

错误:

openBinaryFile: resource exhausted (Too many open files)

代码:

import           Control.Monad.Trans (lift, MonadIO)
import           Control.Monad.Trans.Resource (runResourceT, MonadResource, MonadUnliftIO, ResourceT, liftResourceT)
import qualified Data.ByteString.Streaming          as BSS
import qualified Data.ByteString.Streaming.Char8    as BSSC
import           System.TimeIt

main :: IO ()
main = timeIt $ runResourceT $ dump $ BSS.drop 24 $ BSS.readFile "filename"

dump :: MonadIO m => BSS.ByteString m r -> m ()
dump bs = do
    isEmpty <- BSS.null_ bs
    if isEmpty then return ()
    else do
        BSSC.putStr $ BSS.take 1 bs
        dump $ BSS.drop 1 bs

【问题讨论】:

  • 我不确定它是否会帮助更有知识的人回答您的问题,但是,listenNaiveStreaming 来自哪里?至少它不在hoogle上。此外,imo iterate 使用 LambdaCase 并使用 do 表示法而不是手动 &gt;&gt;=ing 会更容易阅读。
  • 我修正了错字。我不熟悉 LambdaCase;我会调查的!
  • 请注意,流媒体库通常不会加快速度。见stackoverflow.com/a/55814664/7203016
  • @K.A.Buhr 谢谢,实际上其他帖子也是我写的。我仍在努力使这项工作正常进行,但我会在未来的实际项目中牢记您的评论。
  • 我的第一个直觉是每次迭代都会打开一个文件。能不能把readFile "filename"拉出来,只传入文件句柄?

标签: haskell haskell-streaming


【解决方案1】:

使用流式库时,重用有效的流通常是个坏主意。也就是说,您可以将dropsplitAt 之类的函数应用于流,然后继续使用生成的流,或者您可以使用fold 之类的函数将流作为一个整体来使用,这样您就可以基础单子。但是你永远不应该将相同的流值应用于两个不同的函数。

遗憾的是,目前的 Haskell 类型系统无法在编译时强制执行该限制,它需要某种形式的 linear types。相反,它变成了用户的责任。

null_ 函数可能是streaming-bytestring api 中的一个缺陷,因为它不会随结果一起返回新的流,给人的印象是流重用在整个 API 中是正常的。如果它有一个像这样的签名会更好 null_ :: ByteString m r -&gt; m (Bool, ByteString m r).

同样,不要将droptake 用于相同的流值。相反,请使用 splitAtuncons 并使用除法结果。

dump :: MonadIO m => BSS.ByteString m r -> m ()
dump bs = do
    mc <- BSSC.uncons bs -- bs is only used once
    case mc of
        Left _ -> return ()
        Right (c,rest) -> do liftIO $ putChar c
                             dump rest

所以,关于错误。正如@BobDalgleish 在 cmets 中提到的那样,正在发生的事情是在调用null_ 时打开了文件(这是我们第一次从流中“要求”某些东西)。在递归调用中,我们再次传递原始的bs 值,因此它将再次打开文件,每次迭代一次,直到达到文件句柄限制。


就我个人而言,我不喜欢将ResourceT 与流媒体库一起使用。如果可能的话,我更喜欢使用withFile 打开文件,然后使用回调创建和使用流。但有些事情这样更难。

【讨论】:

  • null_ 应该被视为完全消耗流。再次使用它将再次执行最外层的效果。当然应该有一个版本可以生成可以安全使用的新流。你要开票,还是我要开?
  • (isEmpty :> bs)
  • @dfeuer 正如 paperduck 所提到的,该功能似乎已经存在!我没见过。不过,它可能会得到更好的记录。
猜你喜欢
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 2017-12-03
  • 1970-01-01
相关资源
最近更新 更多