簡體   English   中英

從Socket句柄構建的Lazy ByteString不能被懶散地使用和GCed

[英]Lazy ByteString built from Socket handle cannot be consumed and GCed lazily

我正在寫一個網絡文件傳輸應用程序。 使用Lazy ByteString作為中間件

import qualified Data.ByteString.Lazy as BSL

從本地文件構造BSL時,將BSL放入套接字句柄:

BSL.readFile filename >>= BSL.hPut remoteH  -- OK

這很好用。 內存使用量不變。 但是從Socket接收數據,然后寫入本地文件:

BSL.hGet remoteH size >>= BSL.hPut fileH bs  -- starts swapping in 1 second

我可以看到內存使用率持續上升,BSL占用大小字節的內存。 更糟的是,對於超出了我的物理內存的大小尺寸較大,OS立即開始交換。

我必須遞歸地接收ByteStrings的片段。 那沒問題。

為什么BSL表現得那樣?

hGet是嚴格的 - 它立即要求你請求的字節數。 它這樣做是為了便於數據包級讀取。

但是, hGetContentsN是懶惰的,而readFile是根據hGetContentsN

考慮兩個實現:

hGetContentsN :: Int -> Handle -> IO ByteString
hGetContentsN k h = lazyRead -- TODO close on exceptions
  where
    lazyRead = unsafeInterleaveIO loop

    loop = do
        c <- S.hGetSome h k -- only blocks if there is no data available
        if S.null c
          then do hClose h >> return Empty
          else do cs <- lazyRead
                  return (Chunk c cs)

hGet :: Handle -> Int -> IO ByteString
hGet = hGetN defaultChunkSize

hGetN :: Int -> Handle -> Int -> IO ByteString
hGetN k h n | n > 0 = readChunks n
  where
    STRICT1(readChunks)
    readChunks i = do
        c <- S.hGet h (min k i)
        case S.length c of
            0 -> return Empty
            m -> do cs <- readChunks (i - m)
                    return (Chunk c cs)

關鍵的魔力是hGetContentsN的懶惰。

我不能權威地回答延遲字節串的行為,但我建議你研究某種流式方法,比如管道枚舉器 使用管道,您可以編寫如下內容:

import Data.Conduit
import Data.Conduit.Binary

main = do
    let filename = "something"
    remoteH <- getRemoteHandle
    runResourceT $ sourceHandle remoteH $$ sinkFile filename

如果您希望使用network-conduit等 ,您也可以完全繞過Handle抽象:

runResourceT $ sourceSocket socket $$ sinkFile filename

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM