Golang中哪种"out.Close"实现方式更优?

Golang中哪种"out.Close"实现方式更优? 大家好

哪种"out.Close"对语言性能更好?(如你所知,"defer"在循环中效果不佳)

func SaveAvatar() string{
	var size [6][2]uint
	    size[0] = [2]uint{75, 30}
	    size[1] = [2]uint{100, 40}

	for _, v := range size {
		out, err := os.Create("pic/ava/" + "u" + "_" +
			"150000" + "_" + "123" + "_" + strconv.Itoa(int(v[0])) + "." +"JPG")
		if err != nil {
			return "R_FAILED"
		}
		//jpeg.Encode(out, Avatar, nil)

		err =os.Remove("Av" + "_" + strconv.Itoa(int(v[0])) + "." + "JPG")
		if err != nil {
			return "R_FAILED"
		}
		out.Close()
	}
	return "R_Succ"
}
func SaveAvatar() string{
	var size [6][2]uint
	    size[0] = [2]uint{75, 30}
	    size[1] = [2]uint{100, 40}

	for _, v := range size {
		out, err := os.Create("pic/ava/" + "u" + "_" +
			"150000" + "_" + "123" + "_" + strconv.Itoa(int(v[0])) + "." +"JPG")
		if err != nil {
		out.Close()
			return "R_FAILED"
		}
		//jpeg.Encode(out, Avatar, nil)

		err =os.Remove("Av" + "_" + strconv.Itoa(int(v[0])) + "." + "JPG")
		if err != nil {
		out.Close()
			return "R_FAILED"
		}
		out.Close()
	}
	return "R_Succ"
}

这里是代码演示:

https://play.golang.org/p/49u1nJYi8xU

提前感谢。


更多关于Golang中哪种"out.Close"实现方式更优?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

请参考这个讨论。将其包装在匿名函数中可能是一个解决方案

demas

defer 在循环中 - 哪种方式更好?

标签: go

更多关于Golang中哪种"out.Close"实现方式更优?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


out, err := os.Create("pic/ava/" + "u" + "_" + "150000" + "_" + "123" + "_" + strconv.Itoa(int(v[0])) + "." +"JPG")

可以重写为

out, err := os.Create(fmt.Sprintf("pic/ava/u150000_123_%s.JPG", v[0]))

这样稍微简短一些,并且更容易看出用于 os.Create 的路径。对于 os.Unlink 的参数也是同样的处理方式。

你好 Hamed,

有两点需要注意:

  1. 如果打开(或创建)文件失败,那么就没有需要关闭的文件。你不需要调用 out.Close(),因为此时 outnil
  2. 你只需要在文件使用完毕后关闭它。所以请将 os.Close() 紧接在 jpeg.Encode() 之后调用。

尝试这个:https://play.golang.org/p/8W9ezx4UBTf

在循环中处理文件关闭时,第二种实现方式更优,因为它确保了在所有错误返回路径上都正确关闭文件句柄。

第一种实现的问题在于当os.Createos.Remove出错时直接返回,没有关闭已经创建的文件句柄,这会导致文件描述符泄漏。

以下是改进的实现示例:

func SaveAvatar() string {
    sizes := [][2]uint{
        {75, 30},
        {100, 40},
        // ... 其他尺寸
    }

    for _, size := range sizes {
        filename := fmt.Sprintf("pic/ava/u_150000_123_%d.JPG", size[0])
        out, err := os.Create(filename)
        if err != nil {
            return "R_FAILED"
        }

        // 使用defer在函数退出时关闭,但需要包装在匿名函数中
        func() {
            defer out.Close()
            
            // jpeg.Encode(out, Avatar, nil)
            
            removeFilename := fmt.Sprintf("Av_%d.JPG", size[0])
            if err := os.Remove(removeFilename); err != nil {
                return
            }
        }()
    }
    return "R_Succ"
}

或者更明确的错误处理版本:

func SaveAvatar() string {
    sizes := [][2]uint{
        {75, 30},
        {100, 40},
    }

    for _, size := range sizes {
        filename := fmt.Sprintf("pic/ava/u_150000_123_%d.JPG", size[0])
        out, err := os.Create(filename)
        if err != nil {
            return "R_FAILED"
        }

        // 处理编码逻辑
        // if err := jpeg.Encode(out, Avatar, nil); err != nil {
        //     out.Close()
        //     return "R_FAILED"
        // }

        removeFilename := fmt.Sprintf("Av_%d.JPG", size[0])
        if err := os.Remove(removeFilename); err != nil {
            out.Close()
            return "R_FAILED"
        }

        if err := out.Close(); err != nil {
            return "R_FAILED"
        }
    }
    return "R_Succ"
}

第二种方式的优势在于:

  • 确保所有错误路径都关闭文件
  • 避免文件描述符泄漏
  • 代码执行路径清晰

在性能敏感的场景中,还可以考虑批量处理文件操作或使用sync.Pool来优化资源管理。

回到顶部