说一说go的defer和chan

本文阅读 4 分钟
首页 golang 正文

题目序号:2030

题目来源:字节

频次:1

答案:栾龙生

  1. defer

    defer语句用来延时函数的调用,常用于关闭文件描述符、释放锁等资源释放场景。

    defer语句采用后进先出的设计,类似于栈的方式,函数执行时每遇到一个defer都会把一个函数压入栈中,函数返回前再将函数从栈中取出执行,最早被压入栈中的函数最晚被执行。

    不仅函数正常返回会执行被defer延迟的函数,函数中任意一个return语句、panic语句均会触发延迟函数。

    使用场景

    • 释放资源

      m.mutex.Lock()
      defer ,.mutex.Unlock()
    • 流程控制

      var wg wait.Group();
      defer wg.Wait()
    • 异常处理

      defer也常用于处理异常,与recover配合可以消除panic。

      defer func() {recover()}()

    行为规则

    • 延迟函数的参数在defer语句出现时就已经确定

      func a() {
          i := 0
          defer fmt.Println(i)
          i++
          return
      }

      defer语句中的fmt.Println()参数i值在defer出现时就已经确定了,实际上是复制了一份。后面对变量i的修改不会影响fmt.Println()函数的执行,仍然打印0。对于传递指针类型参数,延迟函数的参数是一个地址值,在这种情况下,defer后面的语句对变量的修改可能会影响延迟函数。

    • 延迟函数按后进先出的顺序执行,即先出现的defer最后执行
    • 延迟函数可能操作主函数的具名返回值

      关键字return不是一个原子操作,实际上分两步执行。比如 return i,第一步将i值存入栈中作为返回值,第二步执行跳转,而defer函数的执行时机正是在跳转前,所以defer函数还是有机会操作返回值的。

      func b() (res int) {
          i := 1
          defer func() {
              res++
          }()
          return i
      }
      //output: 2
  2. chan

    管道是go在语言层面上提供的携程间的通信方式。

    声明和初始化管道

    • 变量声明

      var ch chan int
    • 使用内置函数make

      ch1 := make(chan int) //无缓冲管
      ch2 := make(chan int, 5) //有缓冲管道

    管道操作

    • 操作符

      操作符<-代表数据流向,管道在左表示向管道写入数据,管道在右表示从管道中读取数据

      ch := make(chan int, 10)
      ch <- 1 //写数据
      d := <- ch //读数据
    • 数据读写

      管道没有缓冲区时,从管道中读取数据会阻塞,直到有协程向管道中写入数据。同样,向管道中写入数据也会阻塞,直到有协程从管道中读取数据。

      管道中有缓冲区没有数据时,读数据也会阻塞,直到有协程写入数据。向管道中写数据,如果缓冲区已满,那么也会阻塞,直到有协程从缓冲区读数据。

      对于值为nil的管道,无论读写都会阻塞,而且是永久阻塞。

      使用内置函数close()可以关闭管道,尝试向关闭的管道写入数据会触发panic,但关闭的管道仍可读。

      管道表达式最多给两个变量赋值

      v1 := <-ch
      x, ok := <-ch

      第一个变量为读出的数据,第二个变量表示是否成功的读取了数据。

本文来自投稿,不代表本站立场,如若转载,请注明出处:
syncpool的实现原理
« 上一篇 09-17
问了sync.Map(我说我对sync.Pool比较熟,就说Pool了)
下一篇 » 09-17

发表评论

发表评论