如何在资源超出周围函数的范围时推迟资源清理?

huangapple go评论81阅读模式
英文:

How to defer resource cleanup when that resource outlives the scope of the surrounding function?

问题

让我们以这段代码为例,该代码使记录器将日志写入本地文件而不是标准输出:

f, err := os.OpenFile("filename", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
        log.Fatal(err)
}   

defer f.Close()

log.SetOutput(f)

作者将这段代码直接放在main()函数中,这样它就能按预期工作。但是,如果我想将这段代码放入一个专用函数中,然后main()函数调用该函数,那么它将不再起作用,因为在记录器被使用之前,f.Close()将被调用。

例如(如果上面的代码现在在一个名为logToFile()的函数中):

main() {
   logToFile()
   log.Print("I'm going to end up in stdout\n")
}

这段代码能够移动到自己的函数中,并且仍然按预期工作吗?

我在打开/关闭数据库连接方面也遇到了同样的情况。似乎唯一的方法是在main()函数内部执行这两个操作,但我认为如果我们能将代码分成函数,代码看起来会更清晰、更符合单一职责原则。在Go语言中,这样做是否可行?

英文:

Let's take the example of this piece of code that makes the logger write to a local file instead of the standard output:

f, err := os.OpenFile("filename", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
        log.Fatal(err)
}   

defer f.Close()

log.SetOutput(f)

The author is putting this code straight into the main() function, which makes it work as intended. But if I wanted to put this code into a a dedicated function which main() may then call, then it would no longer work, because the f.Close() would be called before the logger ever gets used.

E.g. (if the code above is now in a function called logToFile()):

main() {
   logToFile()
   log.Print("I'm going to end up in stdout\n")
}

Can this be moved into its own function, and still have it work as intended?

I've had the same situation with opening/closing of a database connection. And it seems like the only way is to do both of these things inside the main(), but I think the code would look cleaner and more SoC if we could divide it into functions. Is this a no-no in Go?

答案1

得分: 6

你是不是在寻找这样的代码?

type closerFunc func() error

func logToFile(path string) closerFunc {
    f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal(err)
    }   

    log.SetOutput(f)

    return func() error {
        return f.Close()
    }
}

使用方法

func main() {
    closerFn := logToFile("filename")
    defer closerFn()

    log.Print("logs to file\n")
}

这段代码定义了一个closerFunc类型和一个logToFile函数。logToFile函数接受一个文件路径作为参数,打开该文件并将日志输出到文件中。它返回一个闭包函数,用于关闭文件。在main函数中,我们可以使用logToFile函数来初始化一个closerFn闭包函数,并使用defer关键字在函数结束时关闭文件。然后,我们可以使用log.Print函数将日志输出到文件中。

英文:

You looking for something like this?

type closerFunc func() error

func logToFile(path string) closerFunc {
    f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal(err)
    }   

    log.SetOutput(f)

    return func() error {
        return f.Close()
    }
}

To use:

func main() {
    closerFn := logToFile("filename")
    defer closerFn()

    log.Print("logs to file\n")
}

答案2

得分: 0

一种选择是使用延续传递风格,将要在defer块中执行的代码作为显式参数传递:

func withLogToFile(filename string, body func()) {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	prevOut := log.Writer()
	log.SetOutput(f)
	defer func() {
		log.SetOutput(prevOut)
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	body()
}

然后调用的地方变成:

func main() {
	withLogToFile(filename, func() {
		log.Print("我将会出现在", filename)
	})
}

(https://play.golang.org/p/ebCvtzufU5U)

英文:

One option is to use continuation-passing style, passing the code to be executed within the defer block as an explicit argument:

func withLogToFile(filename string, body func()) {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	prevOut := log.Writer()
	log.SetOutput(f)
	defer func() {
		log.SetOutput(prevOut)
		if err := f.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	body()
}

Then the call site becomes:

func main() {
	withLogToFile(filename, func() {
		log.Print("I'm going to end up in ", filename)
	})
}

(https://play.golang.org/p/ebCvtzufU5U)

huangapple
  • 本文由 发表于 2021年8月7日 23:12:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/68693616.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定