golang之defer

概述

对于资源释放,有很多不同的实现方式,不同语言也有不同的惯用方法。

  • C语言 :手动管理
  • Golang :defer
  • Python :上下文管理器contexManager
  • C++ : 作用域和析构函数
  • Rust :所有权和drop trait

如果了解上面几种语言的童鞋应该知道,

C语言资源管理是比较麻烦的,一旦资源使用过程中出错,就可能造成资源泄漏。

Golang通过defer,即使过程中panic,也可以释放资源。

Python通过上下文管理器,主要是两个magic function`__enter__`, `__exit__`来保证资源的释放。

C++和Rust相似,都是在某种语义下自动调用释放函数。但是Rust有所有权检查,可以防止写代码犯傻 (比如C++不小心拷贝了一下。)

以上来看,C++和Rust的必须要在编程时注意释放的时机,也就是需要程序员更多的思考。但是Rust编译器会帮你一下,而C++并不会。

其次Python和Golang都使用了显式管理,一定不能忘了做,不过做了问题就不大了。

defer

不过这是一篇关于derfer的文章,写一下defer需要注意的重点(就是读Effective Go的一点笔记)。

基本使用

`defer语句将函数调用安排在当前函数结束前执行。也就是defer 语句中的函数调用是当前函数最后执行的东西`

- 一个经典的例子

 1 // Contents returns the file‘s contents as a string.
 2 func Contents(filename string) (string, error) {
 3     f, err := os.Open(filename)
 4     if err != nil {
 5         return "", err
 6     }
 7     defer f.Close()  // f.Close will run when we‘re finished.
 8
 9     var result []byte
10     buf := make([]byte, 100)
11     for {
12         n, err := f.Read(buf[0:])
13         result = append(result, buf[0:n]...) // append is discussed later.
14         if err != nil {
15             if err == io.EOF {
16                 break
17             }
18             return "", err  // f will be closed if we return here.
19         }
20     }
21     return string(result), nil // f will be closed if we return here.
22 }

用上面的话翻译代码中的`defer f.Close()`就是“将f.Close()放在Contents函数的最后执行”

将`f.Close()`放在defer后面有两个好处:保证资源释放、离`Open`比较近不会忘了做。

这没什么好说的,golang必须显式释放资源。

细节

首先明确两个概念

- defer语句执行

将defer语句中函数调用安排在了当前函数结束前执行

- 函数调用执行

运行defer语句中的函数调用

参数求值

这里的参数求值指的是defer语句中函数调用的参数。

参数在defer语句执行求值,而不是在函数调用执行时求值。

- 又一个例子

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}

func main() {
    b()
}

首先看函数b,因为参数是在defer语句执行时求值的,所以`trace("b")`要先被求值[先打印"b",再返回"b"],然后再往下执行,在函数b结束之前会调用`un("b")`.

同理如函数a。现在猜一下执行结果。

entering: b
in b
entering: a
in a
leaving: a
leaving: b

执行顺序

当有多个defer语句的时候,到底是谁的函数调用先执行呢?

1 for i := 0; i < 5; i++ {
2     defer fmt.Printf("%d ", i)
3 }

defer的函数调用按着后进先出(LIFO)的方式执行。大概猜一猜,每次defer都会将函数调用压入栈中,最后依次出栈执行。

最后

golang中defer也就主要用在资源管理上了。明确以上几点问题,应该问题不大了(吹牛ing)。

原文地址:https://www.cnblogs.com/chyuwei/p/11082225.html

时间: 2024-10-02 21:30:39

golang之defer的相关文章

关于golang的defer的练习

golang的defer怎么说.大意就是在函数return后.函数关闭前.按照filo的顺序来执行的关键字 上代码: package main import ( "fmt" ) func main() { // a() fmt.Println(c()) // b() // fmt.Println(d()) } func c() (i int) { defer func() { i++ }() defer fmt.Println("this:", i) return

golang中defer的使用规则

在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用.因此,defer通常用来释放函数内部变量. 为了更好的学习defer的行为,我们首先来看下面一段代码: func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName)if err != nil {retu

轻松掌握golang的defer机制

什么是defer? 如果熟悉python的话,会感觉defer在某种程度上有点类似于python中的上下文管理,golang中的defer是golang提供的一种延迟调用的机制,可以让一个函数在当前函数执行完毕(即使出错)后执行. 因此显然defer对于那些io流操作很有用,因为io流操作结束之后是需要close的,而程序员很容易忘记,所以defer是个好东西,经常用在资源清理.文件关闭.锁释放等等. defer的用法 直接把一个函数调用放在defer后面即可. f, err := os.Ope

golang 之 defer(统计函数执行时间)

1 package main 2 3 import ( 4 "fmt" 5 "time" 6 ) 7 8 func sum(a ...int) int { 9 defer trace("sum")() // note:不要忘记defer语句后的圆括号,否则本该在进入时执行的操作会在退出时执行,而本该在退出时执行的,永远不会执行 10 total := 0 11 for _, val := range a { 12 total += val 13

golang中defer的正确使用方式(源自深入解析go)

3.4 defer关键字 defer和go一样都是Go语言提供的关键字.defer用于资源的释放,会在函数返回之前进行调用.一般采用如下模式: f,err := os.Open(filename) if err != nil { panic(err) } defer f.Close() 如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用. 不过如果对defer的了解不够深入,使用起来可能会踩到一些坑,尤其是跟带命名的返回参数一起使用时.在讲解defer的实现之前先看

golang defer 延后执行什么

对于golang的defer,我们已经知道,defer定义的语句可以延后到函数返回时执行. 经常用在文件的关闭,锁的释放等场景中.而且defer定义的语句即使遇到panic也会执行.这样,可以执行必要的清理工作,甚至使用recover()捕获异常. 以前使用defer经常这样用: defer close() 或者 defer func(){ //do something... }() 本文介绍defer使用函数返回值(也是函数)作为延后执行内容时的情况. 请看例子代码: package main

在退出作用域时做一些事

Boost.ScopeExit库  由于种种原因,C++中没有Java或C#中的try{}finally{}语句,虽然有SEH,可以实现finally效果,但是可定制性太差,不尽人意. SEH实现的finally语句 __try { throw std::bad_exception(); } __finally { cout << "in finally" << endl; } 使用Boost.ScopeExit库可以完美实现这一功能,功能类似于Golang的d

Golang中多用途的defer

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢? A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function returns, Golang的官方时这么定义的. 1.那么在什么情况下会调用defer延迟过的函数呢? 从文档中可以知道主要有两种情况: 当函数执行了return 语句后 当函数处于pan

【GoLang】panic defer recover 深入理解

先等我想清楚golang错误处理 为什么要这么设计的时候 再来更新... Golang这么时尚的语言是没有类似try..catch 这种异常处理机制,而是使用 panic 和 recover处理异常. 其实相当于python的raise. golang的异常处理组合 panic,defer,recover,跟java中的try catch finially是类似的. 但是从语言的用户体验来说,不怎么好. 但考虑到golang的场景基本是系统高性能层面的,这种精准错误处理应该减少那种后遗症bug.