为什么流字节串给我错误“ openBinaryFile:资源已耗尽(打开的文件太多)”?

时间:2019-05-12 20:09:50

标签: haskell haskell-streaming

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

1 个答案:

答案 0 :(得分:2)

在使用流库时,重用有效的流通常是个坏主意。也就是说,您可以将dropgl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 之类的功能应用于流,然后继续处理结果流,或者可以使用fold之类的功能将流作为一个整体使用,这使您留在基本monad中。但是永远不要将相同的流值应用于两个不同的函数。

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

null_函数可能是streaming-bytestring api中的一个缺陷,因为它不会随结果返回新流,给人的印象是流复用在整个API中都是正常的。如果有这样的签名会更好  splitAt

类似地,请勿将droptake用于相同的流值。而是使用splitAtuncons并处理除法的结果。

null_ :: ByteString m r -> m (Bool, ByteString m r)

所以,关于错误。正如@BobDalgleish在评论中提到的那样,实际上是在调用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 时打开了文件(这是我们第一次“请求”流中的内容)。在递归调用中,我们再次传递原始的null_值,因此它将再次打开文件,每次迭代一次,直到达到文件句柄限制。


就我个人而言,我不喜欢将ResourceT与流媒体库一起使用。我更喜欢使用withFile打开文件,然后在可能的情况下通过回调创建和使用流。但是那样做有些事情会更困难。