file-io - Go ioutil 使用太多文件描述符/泄漏?

标签 file-io go unmarshalling deferred ulimit

我正在浏览一个文件列表,并将其中的 xml 数据解码到一个结构数组 rArray 中。我打算处理大约 18000 个文件。当我处理了大约 1300 个文件时,程序出现错误并提示打开的文件过多。如果我将处理的文件数量限制为 1000 个安全数量,程序就不会崩溃。

如下所示,我正在使用 ioutil.ReadFile 读取文件数据。

for _, f := range files {

    func() {
        data, err := ioutil.ReadFile("./" + recordDir + "/" + f.Name())
        if err != nil {
            fmt.Println("error reading %v", err)
            return
        } else {
            if (strings.Contains(filepath.Ext(f.Name()), "xml")) {

                //unmarshal data and put into struct array
                err = xml.Unmarshal([]byte(data), &rArray[a])
                if err != nil {
                    fmt.Println("error decoding %v: %v",f.Name(), err)
                    return
                }
            }
        }
    }()
}

我不确定 Go 是否使用了过多的文件描述符或没有足够快地关闭文件。

看完https://groups.google.com/forum/#!topic/golang-nuts/7yXXjgcOikM并查看 http://golang.org/src/pkg/io/ioutil/ioutil.go 中的 ioutil 源代码,ioutil.ReadFile 的代码显示它使用 defer 关闭文件。 defer 在返回调用函数且 ReadFile() 是调用函数时运行。我的理解是否正确? 我还尝试将代码的 ioutil.ReadFile 部分包装在一个函数中,但这没有任何区别。

我的 ulimit 设置为无限制。

更新: 我相信在我的解压缩功能期间实际上发生了太多文件的错误。

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }

    for _, f := range r.File {
        rc, err := f.Open()
        if err != nil {
            panic(err)
        }

        path := filepath.Join(dest, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            f, err := os.OpenFile(
                path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                panic(err)
            }

            _, err = io.Copy(f, rc)
            if err != nil {
                panic(err)
            }
            f.Close()
        }
        rc.Close()
    }
    r.Close()
    return nil
}

我最初从 https://gist.github.com/hnaohiro/4572580 得到了 Unzip 函数, 但经过进一步检查,在 gist 作者的函数中使用 defer 似乎是错误的,因为文件只会在 Unzip() 函数返回后关闭,但为时已晚那么将打开 18000 个文件描述符。 ;)

我用显式 Close() 替换了延迟的 Close,如上所示,但我仍然收到相同的“打开的文件太多”错误。是不是我修改的解压功能有问题?

更新#2 哎呀,我在 Heroku 上运行它,并且一直将我的更改推送到错误的应用程序。经验教训:在 heroku toolbelt 中验证目标应用。

https://gist.github.com/hnaohiro/4572580解压代码 工作,因为它在处理所有文件之前不会关闭文件。

我在上面明确关闭的解压缩代码有效,@peterSO 的回答中的延迟版本也是如此。

最佳答案

我会修改 https://gist.github.com/hnaohiro/4572580 中的 Unzip 函数到以下内容:

package main

import (
    "archive/zip"
    "io"
    "log"
    "os"
    "path/filepath"
)

func unzipFile(f *zip.File, dest string) error {
    rc, err := f.Open()
    if err != nil {
        return err
    }
    defer rc.Close()

    path := filepath.Join(dest, f.Name)
    if f.FileInfo().IsDir() {
        err := os.MkdirAll(path, f.Mode())
        if err != nil {
            return err
        }
    } else {
        f, err := os.OpenFile(
            path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
        if err != nil {
            return err
        }
        defer f.Close()

        _, err = io.Copy(f, rc)
        if err != nil {
            return err
        }
    }
    return nil
}

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()

    for _, f := range r.File {
        err := unzipFile(f, dest)
        if err != nil {
            return err
        }
    }

    return nil
}

func main() {
    err := Unzip("./sample.zip", "./out")
    if err != nil {
        log.Fatal(err)
    }
}

关于file-io - Go ioutil 使用太多文件描述符/泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24197011/

相关文章:

go - 实例新类型(Golang)

http - Golang RedirectHandler() 返回接口(interface),以 *http.redirectHandler 结尾

casting - 如何将未编码的 Golang 对象转换为指定变量的类型

Java UnMarshall XML 给出 null 对象

Java:是否可以打开一个文件进行写入,但如果文件已经存在则爆炸?

C++读取txt文件的CSV值

python - 将列表的元素打印到python中的单独文本文件中

java - 如何在Java中连续检查文件是否存在? [复制]

go - 在 Debian Docker 镜像中使用 librdkafka 构建 Golang 应用程序?

java - 使用 JAXB 解码在下面的代码中给出 null 吗?