• Goroutine和Panic


    一. Goroutine

    Goroutine是Golang2个核心的设计之一,Goroutine在Golang里面指的是协程。我们知道线程属于系统层面,通常来说创建一个新的线程会消耗较多的资源且管理不易。而Goroutine就像轻量级的线程,但我们称其为协程,一个Go程序可以运行超过数万个Goroutine,并且这些性能都是原生级的,随时都能够关闭、结束。

    在内置的官方包中也不时能够看见Goroutine的应用,像是net/http中用来监听网络服务的函数实际上是创建一个不断运行循环的Goroutine。

    我们可以通过runtime.GOMAXPROCS来设置同时执行的最大CPU数,GO默认是使用一个CPU核的,除非设置runtime.GOMAXPROCS
    那么在多核环境下什么情况下设置runtime.GOMAXPROCS会比较好的提高速度呢?适合于CPU密集型、并行度比较高的情景。如果是IO密集型,CPU之间的切换也会带来性能的损失。

    func main() {
        num := runtime.NumCPU()    # 本地机器的逻辑CPU个数
        runtime.GOMAXPROCS(num)    # 设置可同时执行的最大CPU数
        fmt.Println(num)
    }
     

    二. 并发

    Goroutine最大的一个功能就是实现高并发,通过Goroutine能够让程序以异步的方式运行,而不需要担心一个函数导致程序中断,因此Go语言也非常地适合网络服务。

    我们通过go让其中一个函数异步运行,如此就不需要等待该函数运行完后才能运行下一个函数。

    func main() {
        # 通过 `go` 我们可以把这个函数异步执行,这样就不会阻塞往下执行。
        go loop()
        # 执行 Other
    }


    三. Recover

    虽然Goroutine能够实现高并发,但是如果某个Goroutine panic了,而且这个Goroutine里面没有捕获recover,那么整个进程就会挂掉。所以,好的习惯是每当go产生一个goroutine,就需要写下recover。

    func domainPut(num int) {
        defer func() {
            err := recover()
            if err != nil {
                fmt.Println("panic error.")
            }
        }()
        panic("error....")
    }

    func main() {
        for i := 0; i < 10; i++ {
            domainName := i
            go domainPut(domainName)
        }
        time.Sleep(time.Second * 2)
    }

    四. 规范

    使用Goroutine实现高并发有一些规范开发必须要注意,否则很容易带来困扰
    1. Golang主程序必须要等待所有的Goroutine结束才能够退出,否则如果先退出主程序会导致所有的Goroutine可能未执行结束就退出了

    package main

    import (
        "sync"
        "fmt"
        "time"
    )

    func calc(w *sync.WaitGroup, i int)  {
        defer func() {
            err := recover()
            if err != nil {
              fmt.Println("panic error.")
            }
        }()
        
        fmt.Println("calc: ", i)
        time.Sleep(time.Second)
        w.Done()
    }

    func main()  {
        # WaitGroup能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成。
        wg := sync.WaitGroup{}    
        for i:=0; i<10; i++ {
            wg.Add(1)
            go calc(&wg, i)
        }
        # 阻塞主线程等到所有的goroutine执行完成
        wg.Wait()
        fmt.Println("all goroutine finish")
    }


    2. 每个Goroutine都要有recover机制,因为当一个Goroutine抛panic的时候只有自身能够捕捉到其它Goroutine是没有办法捕捉的。如果没有recover机制,整个进程会crash。
    3. recover只能在defer里面生效,如果不是在defer里调用,会直接返回nil。
    4. Goroutine发生panic时,只会调用自身的defer,所以即便主Goroutine里写了recover逻辑,也无法recover。

    # Goroutine里面需要写defer recover机制
    go func() {
       defer func() {
          err := recover()
          if err != nil {
             fmt.Println("panic error.")
          }
       }()
       # other code
    }()
    ————————————————
    原文链接:https://blog.csdn.net/chenguolinblog/article/details/90665080

  • 相关阅读:
    1.2 文本域(含可编辑表格实现)
    JS手册目录
    1.1 文本框
    JS传中文到后台需要的处理
    java基础和面向对象面试题_01
    try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
    java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
    java基础学习_IO流04_用户登录注册案例(IO版)、数据操作流(操作基本数据类型的流)、内存操作流、打印流、标准输入输出流、随机访问流、合并流、序列化流(对象操作流)、Properties属性集合类、NIO(新IO)_day22总结
    思想:java中,父类的方法中传入的形参的数据类型是泛型,子类的方法的形参想只要一种确定的数据类型,子类该如何做呢?
    几种后端开发技术的选型调研
  • 原文地址:https://www.cnblogs.com/ithubb/p/14636238.html
Copyright © 2020-2023  润新知