当前位置:网站首页>Go语言学习教程(十六)

Go语言学习教程(十六)

2022-07-05 18:43:00 小陈工

一、死锁

* 在主goroutine中向无缓存channel添加内容或在主goroutine中向channel添加内容且添加内容的个数已经大于channel缓存个数就会产生死锁

    fatal error : all goroutines are asleep -deadlock!

* 死锁:在程序中多个进程(Golang中goroutine)由于相互竞争资源而产生的阻塞(等待)状态,而这种状态一直保持下去,此时称这个线程是死锁状态

* 在Golang中使用无缓存channel时一定要注意.以下是一个最简单的死锁程序

    * 主协程中有ch<-1,无缓存channel无论添加还是取出数据都会阻塞goroutine,当前程序无其他代码,主goroutine会一直被阻塞下去,此时主goroutine就是死锁状态

    func main() {

       ch := make(chan int)

       ch <- 1

    }

* 而下面代码就不会产生死锁

    * 通过代码示例可以看出,在使用无缓存channel时,特别要注意的是在主协程中要操作channel代码

    func main() {

       ch := make(chan int)

       go func() {

          ch <- 1

          fmt.Println("执行goroutine")

       }()

       time.Sleep(5e9)

       fmt.Println("程序执行结束")

    }

二、有缓存通道

* 创建一个有缓存通道

func main() {

   ch := make(chan int, 3) //缓存大小3,里面消息个数小于等于3时都不会阻塞goroutine

   ch <- 1

   ch <- 2

   ch <- 3

   ch <- 4 //此行出现死锁,超过缓存大小数量

}

* 在Golang中有缓存channel的缓存大小是不能改变的,但是只要不超过缓存数量大小,都不会出现阻塞状态

    func main() {

       ch := make(chan int, 3) //缓存大小3,里面消息个数小于等于3时都不会阻塞goroutine

       ch <- 1

       fmt.Println(<-ch)

       ch <- 2

       fmt.Println(<-ch)

       ch <- 3

       ch <- 4

       fmt.Println(len(ch))//输出2,表示channel中有两个消息

       fmt.Println(cap(ch))//输出3,表示缓存大小总量为3

    }

三、select简介

* Golang中select和switch结构特别像,但是select中case的条件只能是I/O

* select 的语法(condition是条件)

    select{

      case condition:

      case condition:

      default:

    }

* select执行过程:

    * 每个case必须是一个IO操作

    * 哪个case可以执行就执行哪个

    * 多个case都可以执行,随机执行一个

    * 所有case都不能执行时,执行default

    * 所有case都不能执行,且没有default,将会阻塞

func main() {

   runtime.GOMAXPROCS(1)

   ch1 := make(chan int, 1)

   ch2 := make(chan string, 1)

   ch1 <- 1

   ch2 <- "hello"

   select {

   case value := <-ch1:

      fmt.Println(value)

   case value := <-ch2:

      fmt.Println(value)

   }

}

* select多和for循环结合使用,下面演示出了一直在接收消息的例子

func main() {

    ch := make(chan int)

    for i := 1; i <= 5; i++ {

        go func(arg int) {

            ch <- arg

        }(i)

    }

  //如果是一直接受消息,应该是死循环for{},下面代码中是明确知道消息个数

    for i := 1; i <= 5; i++ {

        select {

        case c := <-ch:

            fmt.Println("取出数据", c)

        default:

            //没有default会出现死锁

        }

    }

    fmt.Println("程序执行结束")

}

* break可以对select生效,如果for中嵌套select,break选择最近结构

四、GC

* GC英文全称 garbage collector

* Go语言GC是相对C/C++语言非常重要的改进

* 一些常用GC算法

    * 引用计算法:当对象被引用时计算器加一.不被引用计数器减一

        * PHP和Object-C使用

        * 相互引用无法回收

        * 计数增加消耗

    * Mark And Sweep 标记和清除算法:停止程序运行,递归遍历对象,进行标记。标记完成后将所有没有引用的对象进行清除

        * 由于标记需要停止程序(Stop the world),当对象特别多时,标记和清除过程比较耗时(可能几百毫秒),很难接受

    * 三色标记法:是Mark And Sweep的改进版.从逻辑上分为白色区(未搜索),灰色区(正搜索),黑色区(已搜索).灰色区内容是子引用没有进行搜索,黑色区表示子引用存在

    * 分代收集:一般情况都有三代,例如java中新生代,老年代,永久代.当新生代中带有阈值时会把对象放入到老年代,相同道理老年代内容达到阈值会放入到永久代

五、Go语言中的GC

* Go语言中采用Stop The World方式

* Golang每个版本基本上都会对GC进行优化,从Golang1.5开始支持并发(concurrent )收集,从1.8版本已经把STW时间优化到了100微妙,通常只需要10微秒以下.且在1.10版本时再次优化减少GC对CPU占用

* Go语言中GC是自动运行的,在下列情况下会触发GC

    * 当需要申请内存时,发现GC是上次GC两倍时会触发

    * 每2分钟自动运行一次GC

* GC调优

    * 小对象复用,局部变量尽量少声明,多个小对象可以放入到结构体,方便GC扫描

    * 少用string的”+”

原网站

版权声明
本文为[小陈工]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_40652101/article/details/125624436