File io 使用过多文件描述符转到ioutil/泄漏?

File io 使用过多文件描述符转到ioutil/泄漏?,file-io,go,unmarshalling,deferred,ulimit,File Io,Go,Unmarshalling,Deferred,Ulimit,我正在浏览一个文件列表,并将其中的xml数据解压成一个结构数组rArray。我打算处理大约18000个文件。当我处理了大约1300个文件时,程序惊慌失措,说打开的文件太多了。如果我将处理的文件数量限制为1000个,那么程序不会崩溃 如下所示,我正在使用ioutil.ReadFile读取文件数据 for _, f := range files { func() { data, err := ioutil.ReadFile("./" + recordDir + "/" +

我正在浏览一个文件列表,并将其中的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是否使用了太多的文件描述符,或者关闭文件的速度不够快

在中读取并查看
ioutil
源代码后,
ioutil.ReadFile
的代码显示它使用
defer
关闭文件<返回调用函数时,code>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
}
我最初从中获得了
Unzip
函数,但经过进一步检查,在gist author函数中使用
defer
似乎是错误的,因为只有在
Unzip()
函数返回后,文件才会关闭,这太晚了,因为到时候18000个文件描述符将被打开

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

更新#2 哎呀,我在Heroku上运行这个程序,一直在用我的更改推错应用程序。经验教训:在heroku toolbelt中验证目标应用程序

从中解压缩代码不起作用,因为在处理所有文件之前,它不会关闭文件


我的解压代码和上面显式的close都可以工作,@peterSO的答案中的defer版本也是如此

我会将解压函数从修改为以下内容:

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)
    }
}

这很奇怪,什么版本的围棋(
围棋版本
)?另外,将
ioutil.ReadFile
替换为
f,err:=os.Open(…)
/
延迟f.Close()
/
ioutil.ReadAll
帮助?@其中一个,我将原因缩小到了我拥有的另一个函数,但仍存在上述更新中描述的问题。我目前正在使用go 1.2.2。谢谢。也许可以在
/proc/$pid/fd
中查看一下应用程序中打开了哪些文件(它应该包含表示每个打开的文件描述符的符号链接)。这可能会指示哪些描述符被泄漏。您是否有可能对每个文件使用go例程?是的,我认为问题在于打开文件的goroutine太多,并让调度器处理。什么是代码逻辑?嗯,这是有效的(以及我以前的显式版本),但是你能解释一下为什么
unzipFile
方法不能保持N个文件描述符打开吗?我以为
unzipFile
中的
defer
s只会在unzipFile返回时运行?我对此很感兴趣。谢谢非常好的unzipFile分离-它确保每个文件将得到快速关闭。