File io 使用过多文件描述符转到ioutil/泄漏?
我正在浏览一个文件列表,并将其中的xml数据解压成一个结构数组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 + "/" +
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分离-它确保每个文件将得到快速关闭。