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
答案 0 :(得分:2)
在使用流库时,重用有效的流通常是个坏主意。也就是说,您可以将drop
或gl.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
。
类似地,请勿将drop
和take
用于相同的流值。而是使用splitAt
或uncons
并处理除法的结果。
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
打开文件,然后在可能的情况下通过回调创建和使用流。但是那样做有些事情会更困难。