zoukankan      html  css  js  c++  java
  • 9.7Go之函数之defer(延迟执行语句)

    9.7Go之函数之defer(延迟执行语句)

    defer的特点

    • defer 语句会将其后面跟随的语句进行延迟处理

    • 在一个函数中先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行

    作用场景:

    • 用于释放某些已分配的资源。如互斥锁、关闭文件等

    defer的用法类似于java里面的finally语句块--->try...catch...finally

    多个defer语句特点

    • 类似于栈,先入后出、后入先出

    示例:

    package main

    import "fmt"

    func main() {
    fmt.Println("Begin")

    //使用defer语句类似于栈指针
    defer fmt.Println(1)

    defer fmt.Println(2)

    defer fmt.Println(3) //此时这个函数是最后入栈的,相当于位于栈顶。先出

    fmt.Println("End")
    }

    注意:

    • 延迟调用是在 defer 所在函数结束时进行,函数结束可以是正常返回时,也可以是发生宕机时

    使用延迟语句在函数退出时释放资源

    处理开、关文件,接收、回复请求,加锁、解锁等操作容易忽略资源的释放

    defer 语句是在函数退出时执行的语句,使用 defer 能非常方便地处理资源释放问题

    • 使用延迟并发解锁

    • 使用延迟释放文件句柄


    使用延迟并发解锁

    示例:

    并发使用 map,使用 sync.Mutex 进行加锁,防止竞态问题

    package main

    import (
    "fmt"
    "sync"
    )

    var (
    //声明一个map
    valueKey = make(map[string]int)
    valueKeyGuard sync.Mutex
       /*
       map 默认不是并发安全的,准备一个 sync.Mutex 互斥量保护 map 的访问
       */
    )

    /*
    声明一个函数,读取map里面的键和值,返回值,并且对竞争的资源加锁
    */
    func readValue(key string) int {
    //加上资源锁
    valueKeyGuard.Lock()
       /*
       使用互斥量加锁
       */

    v := valueKey[key]

    //资源解锁
    valueKeyGuard.Unlock()
       /*
       使用互斥量解锁
       */

    return v
    }

    func main() {
    valueKey["name"] = 1
    //调用readValue方法
    fmt.Println(readValue("name"))
    }

    使用defer简化readValue函数

    /*
    使用defer简化上面的方法
    */
    func readValue2(key string) int {
    //资源加锁
    valueKeyGuard.Lock()
       /*
       使用互斥量加锁
       */

    //使用defer让释放所最后执行
    defer valueKeyGuard.Unlock()
       /*
       该语句不会马上执行,而是等 readValue() 函数返回时才会被执行
       */

    //返回value
    return valueKey[key]
       /*
       从 map 查询值并返回的过程中,与不使用互斥量的写法一样
       */
    }

    使用延迟释放文件句柄

    操作一个文件的流程:

    • 打开文件资源

    • 获取、操作文件资源

    • 关闭文件资源

    每一步系统操作都需要进行错误处理,而每一步处理都会造成一次可能的退出。因此要关注函数退出处正确释放资源

    查询文件示例:

    package main

    import (
    "fmt"
    "os"
    )

    func fileSize(fileName string) int64 {
    //根据文件名打开文件, 返回文件句柄和错误
    f, err := os.Open(fileName)
       /*
       使用 os 包提供的函数 Open(),根据给定的文件名打开一个文件,并返回操作文件用的句柄和操作错误
       */

    if err != nil {
    return 0
    }

    //获取文件状态
    info, err := f.Stat()
    if err != nil {
    //关闭资源
    f.Close()
    return 0
    }
       /*
       处理错误,此时文件正常打开,要释放资源要调用f的close方法关闭文件
       */

    //取文件大小
    size := info.Size()

    //关闭资源
    f.Close()

    //返回名称和大小
    return size
    }

    func main() {
    fmt.Println(fileSize("APITest.jar"))
    }

    使用defer改良上面的函数:

    /*
    使用defer简化上面的函数。这里要注意:
    1、defer是放在函数中最后执行的语句,相当于位于栈底
    2、defer的声明式显式的
    */
    func fileSize2(fileName string) int64 {
    //打开资源
    f, err:=os.Open(fileName)

    if err != nil {
    //返回0
    return 0
    }

    //正常打开资源接下来就是获取资源
    //声明一个遇到异常关闭函数的释放资源的方法
    defer f.Close()
       /*
       使用 defer,将 f.Close() 延迟调用
       不能将这一句代码放在上一个if判断前面,一旦文件打开错误,f 将为空,在延迟语句触发时,将触发宕机错误。
       defer 后的语句(f.Close())将会在函数返回前被调用,自动释放资源
       */

    //获取文件状态
    info, err:=f.Stat()
    if err != nil {
    //此时会结束函数,结束前会运行defer标记的内容,所以这里无需声明f.close()
    return 0
    }

    //获取文件大小
    size := info.Size()

    //返回size
    return size
    }

    章节小结:

    • 主要学习了defer的特性,用于延迟调用语句。

    • 多个由defer标记的语句相当于一个入栈模型,先入后出、后入先出

    • defer是在函数返回值返回前调用。特别注意。

  • 相关阅读:
    TCP 的那些事儿(转载)
    3. 对象在内存中的布局
    GO语言学习之数据类型-->基本类型(字符串)
    GO语言学习之变量and常量
    wrk
    为什么显示消息“错误:您所在国家/地区是禁运国,无法下载 Java”?
    raw.githubusercontent.com 访问不了
    Windows Terminal
    vue:无法加载文件C:UsersAppDataRoaming pmvue.ps1, 在此系统上无法加载脚本
    vue使用过滤改变el-switch开关的状态
  • 原文地址:https://www.cnblogs.com/JunkingBoy/p/15239768.html
Copyright © 2011-2022 走看看