导管:产生内存泄漏

时间:2016-03-28 01:45:05

标签: haskell memory-leaks conduit attoparsec

对上一个问题(haskell-data-hashset-from-unordered-container-performance-for-large-sets)进行一些观察我偶然发现了一个奇怪的内存泄漏

module Main where

import System.Environment (getArgs)
import Control.Monad.Trans.Resource (runResourceT)
import Data.Attoparsec.ByteString (sepBy, Parser)
import Data.Attoparsec.ByteString.Char8 (decimal, char)
import Data.Conduit
import qualified Data.Conduit.Attoparsec as CA
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL

main :: IO ()
main = do (args:_) <- getArgs
          writeFile "input.txt" $ unlines $ map show [1..4 :: Int]
          case args of "list" -> m1
                       "fail" -> m2
                       "listlist" -> m3
                       "memoryleak" -> m4
                       --UPDATE
                       "bs-lines":_ -> m5
                       "bs":_ -> m6
                       _ -> putStr $ unlines ["Usage: conduit list"
                                             ,"               fail"
                                             ,"               listlist"
                                             ,"               memoryleak"
                                             --UPDATE
                                             ,"               bs-lines"
                                             ,"               bs"
                                             ]
m1,m2,m3,m4 :: IO ()
m1 = do hs <- runResourceT
            $  CB.sourceFile "input.txt"
            $$ CB.lines
           =$= CA.conduitParser (decimal :: Parser Int)
           =$= CL.map snd
           =$= CL.consume
        print hs
m2 = do hs <- runResourceT
            $  CB.sourceFile "input.txt"
            $$ CA.conduitParser (decimal :: Parser Int)
           =$= CL.map snd
           =$= CL.consume
        print hs
m3 = do hs <- runResourceT
            $  CB.sourceFile "input.txt"
            $$ CB.lines
           =$= CA.conduitParser (decimal `sepBy` (char '\n') :: Parser [Int])
           =$= CL.map snd
           =$= CL.consume
        print hs
m4 = do hs <- runResourceT
            $  CB.sourceFile "input.txt"
            $$ CA.conduitParser (decimal `sepBy` (char '\n') :: Parser [Int])
           =$= CL.map snd
           =$= CL.consume
        print hs
-- UPDATE
m5 = do inpt <- BS.lines <$> BS.readFile "input.txt"
        let Right hs =  mapM (parseOnly (decimal :: Parser Int)) inpt
        print hs
m6 = do inpt <- BS.readFile "input.txt"
        let Right hs =  (parseOnly (decimal `sepBy` (char '\n') :: Parser [Int])) inpt
        print hs

以下是一些示例输出:

$ > stack exec -- example list
[1234]
$ > stack exec -- example listlist
[[1234]]
$ > stack exec -- conduit fail
conduit: ParseError {errorContexts = [], errorMessage = "Failed reading: takeWhile1", errorPosition = 1:2}
$ > stack exec -- example memoryleak
(Ctrl+C)

-- UPDATE
$ > stack exec -- example bs-lines
[1,2,3,4]
$ > stack exec -- example bs
[1,2,3,4]

现在我的问题是:

  • 为什么m1没有生成[1,2,3,4]
  • 为什么m2失败?
  • 为什么m4与其他所有版本相比完全不同并产生空间泄漏?

2 个答案:

答案 0 :(得分:4)

  

为什么m2失败?

作为字符流的输入文件是:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void btnAdd_Click(object sender, EventArgs e)
    {
        SqlConnection myConnection = new SqlConnection("Server=PH-IT-CC1@SQLEXPRESS;Database=WaitListData;Trusted_Connection=True");
        myConnection.Open();

        SqlCommand myCommand = new SqlCommand("INSERT INTO Table (Firstname, Lastname, Email, Nationality, Time_Of_Play) " + "VALUES ('" + txtFirstname1.Text + "' , '" + txtLastname1.Text + "' , '" + txtEmail1.Text + "' , '" + txtNationality1.Text + "' , '" + txtTimeOfPlay1.Text + "')", myConnection);

        myCommand.ExecuteNonQuery();
        myConnection.Close();
    }
}
}

由于1\n2\n3\n4\n 解析器不需要换行符,因此在使用第一个数字后剩余的流是:

decimal

由于输入流没有用完,\n2\n3\n4\n 将再次在流上运行解析器,这次它甚至不能使用第一个字符,因此失败了。

  

为什么m4与所有其他版本相比表现完全不同并产生空间泄漏?

conduitParser只会在两个整数之间消耗decimal `sepBy` (char '\n'),在成功解析四个数字后,输入流中只有一个字符:

\n

并且\n 无法使用它,更糟糕的是它不会失败:decimal `sepBy` (char '\n')不会消耗任何内容并返回空列表。因此,它无限地解析任何东西,永远不会终止。

  

为什么m1不生产[1,2,3,4]?

我也想知道!我想这与融合有关,也许你应该联系管道包的作者,他刚刚评论了你的问题。

答案 1 :(得分:2)

要回答有关m1的问题:当您使用CB.lines时,您需要转换看起来像的输入:

["1\n2\n3\n4\n"]

成:

["1", "2", "3", "4"]

然后,attoparsec解析&#34; 1&#34;,等待更多输入,看到&#34; 2&#34;等等。