多多读书
904 字
5 分钟
差点被ChatGPT骗了,Golang defer的使用

ChatGPT 是一个强大的 AI 聊天工具,可以在编程方面起到重要的辅助作用。它能够帮助编写代码、查找错误和撰写注释,有效提升工作效率。然而,我们需要保持一定的辨别能力,因为 ChatGPT 偶尔也会提供迷惑性的答案,如果我们没有足够的辨别能力,可能就会被误导。今天,问了 ChatGPT 下面代码的执行结果。

func testDefer() {
	go func() {
		// 协程 1
		fmt.Println("协程 1 开始执行")
		time.Sleep(1 * time.Second)
		fmt.Println("协程 1 结束执行")
	}()

	go func() {
		// 协程 2
		fmt.Println("协程 2 开始执行")
		time.Sleep(2 * time.Second)
		fmt.Println("协程 2 结束执行")
	}()

	defer func() {
		fmt.Println("defer 语句执行")
	}()
}

ChatGPT 给到的答案是:

协程 2 开始执行
协程 1 开始执行
defer 语句执行
协程 1 结束执行
协程 2 结束执行

而我在实际代码测试中却是这样的:

defer 语句执行
协程 1 开始执行
协程 2 开始执行
协程 1 结束执行
协程 2 结束执行

这样就有点令人疑惑了,到底哪个是正确的?接下来我们看看为什么会出现这样的结果。

defer关键字#

defer用于延迟执行一个函数或方法或匿名函数,直到当前函数返回为止。defer的主要用途是用于清理资源,比如关闭打开的文件、网络连接、数据库句柄等。这样可以减少错误的可能性,因为关闭资源的调用和打开资源的调用可以放在一起,更容易看出逻辑。

如果没有 defer 关键字,写入文件的内容的 Go 代码也许就是这样:

func writeToFile(filename, content string) error {
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}

	_, err = file.WriteString(content)
	if err != nil {
		file.Close() // 调用 Close
		return err
	}

  // 更多操作...

	file.Close() // 调用 Close
	return nil
}

上面的代码中,每次函数退出前都要手动调用file.Close()来关闭文件。这样的代码不够简洁和清晰,而且容易忘记调用,严重情况下可能导致文件资源泄漏。但是有了defer可以很简单地避免这些问题。

func writeToFile(filename, content string) error {
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}

  defer file.Close() // 使用defer调用Close方法

	_, err = file.WriteString(content)
	if err != nil {
		return err
	}

  // 更多操作...

	return nil
}

通过使用 defer 调用file.Close()方法,无论是正常退出还是发生异常,都能够正确地关闭文件,再也不用担心是否忘记调用了。

go关键字#

go关键字用于启动一个新的协程(goroutine)。协程是 Go 语言提供的轻量级线程,可以并发执行,由 Go 运行时(Go runtime)负责调度。使用go启动一个新的协程,主程序不会等待协程的执行结果,而是会继续执行后续的代码。

那么回到testDefer函数,这个函数有两个go语句,分别启动了两个协程,并且后面有一个defer语句,会在函数返之前执行。两个协程执行是异步的,不会阻塞后面的代码执行,所以当执行defer语句时,无法确定协程 1 和协程 2 是否开始打印输出,这个取决于它们的调度情况。

所以结论是,defer 语句执行可以在协程 1 开始执行协程 2 开始执行之前执行,也可以在后面执行,这三者的顺序是不确定的。

差点被ChatGPT骗了,Golang defer的使用
https://fuwari.vercel.app/posts/20231016/
作者
我也困了
发布于
2023-10-16
许可协议
CC BY-NC-SA 4.0