七叶笔记 » golang编程 » Golang中的defer语句是如何工作的?

Golang中的defer语句是如何工作的?

在Golang中,defer 语句是在函数返回之前执行一段代码的便捷方式(也就是执行defer语句通常是先将defer放入栈中,待其他代码执行完毕需要return结果时,才执行defer语句中的内容),常用于关闭连接、捕获异常、打印日志等。

如果一个函数中有多个defer语句,则defer执行时,会采取后进先出( LIFO )的顺序策略。如下:

LIFO内部实现

Golang运行时使用 链表 来实现defer的LIFO策略,每一个_defer结构都有一个链接到下一个defer的link属性,如下:

当一个新的defer方法被创建时,它将会被附加到当前 go routine上,同时前一个defer会被链接到新的defer上作为下一个要执行的函数,如下:

然后,当连续调用的时候会从顶部一层一层地去执行这些defer函数,如下:

不过,正如我们所看到的,golang没有循环地去执行defer函数,而是把defer函数一一地拆开,生成如下的 ASM 代码:

从图中我们可以看到,方法 defer proc 被调用了两次,而且内部调用了我们之前看到的newdefer方法来将我们的函数注册为延迟方法。然后,在函数结束时,由于deferreturn函数,deferred方法将被一一调用。

延迟和返回值

defer函数是不能直接返回执行结果的,要访问返回结果的唯一方法是使用变量, 如下:

利用这个特性我们可以将其与recover混合使用来恢复goroutine中的panic。

实际上,在前面的内容中,我们已经看到了_defer结构上除了有一个link属性之外,还有一个_panic 属性,它也是一个结构体。

在调用defer函数之前,golang运行时会调用 gopanic 方法以防出现恐慌:

下面是一个使用命名结果参数来恢复panic的示例:

我们可以看到,通过defer和recover的结合,我们可以不仅可以恢复功能,而且还可以返回错误。

相关文章