• Go语言学习教程(十六)


    一、死锁

    * 在主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的”+”

  • 相关阅读:
    Redis的缓存更新策略和缓存问题
    4.2 索引及其操作
    【OCRA学习】在linux系统安装ORCA,并与xtb联用配置
    数据中心绿色低碳重要趋势下,希捷用创新技术让磁盘“重生”
    好题分享
    如何批量新建文件夹并重命名
    微服务项目:尚融宝(13)(前端平台:搭建管理平台前端程序)
    AI类APP上线需要注意的问题
    MySQL 你所不知道的 SQL 使用技巧
    基于新版本Gradle上传jitpack开源项目
  • 原文地址:https://blog.csdn.net/qq_40652101/article/details/125624436