我正在检查文件列表,并将其中的 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 并查看
ioutil
源代码后 http://golang.org/src/pkg/io/ioutil /ioutil.go,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()
,如上所示,但仍然收到相同的“打开文件过多”错误。难道我修改的Unzip功能有问题?更新#2 糟糕,我在 Heroku 上运行这个程序,并且一直将我的更改推送到错误的应用程序。经验教训:在heroku工具带中验证目标应用程序。
从 https://gist.github.com/hnaohiro/4572580 解压代码确实不工作,因为在处理完所有文件之前它不会关闭文件。
我上面显式关闭的解压缩代码有效,@peterSO 的答案中的延迟版本也有效。
我会将 Unzip 函数从 https://gist.github.com/hnaohiro/4572580 修改为以下内容:
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)
}
}