• golang之goroutine和channel


    多线程程序在单核上运行,就是并发

    多线程程序在多核上运行,就是并行

    Go协程和Go主线程

      Go主线程(线程):一个Go线程上,可以起多个协程 ,你可以这样理解,协程是轻量级的线程

      Go协程的特点:

        1)有独立的栈空间

        2)共享程序堆空间

        3) 调度由用户控制

        4)协程是轻量级的线程3

    goroutine快速入门

    func test() {
        for i := 1; i <= 10; i++ {
            fmt.Println("test() hello, world " + strconv.Itoa(i))
            time.Sleep(time.Second)
        }
    }
    
    func main() {
        go test()
    
        for i := 1; i <= 10; i++ {
            fmt.Println("main() hello, world " + strconv.Itoa(i))
            time.Sleep(time.Second)
        }
    }

      小结:

        1)主线程是一个物理线程,直接作用在cpu上的,是重量级的,非常耗费cpu资源;

        2)协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小;

        3)Golang的协程机制是重要的特点,可以轻松的开启上万个协程。其它编程语言的并发机制是一般基于线程的,开启过多的线程,资源耗费大,这里就突显Golang在并发上的优势了。

    MPG模式基本介绍

      1)M:操作系统的主线程(是物理线程)

      2)P:协程执行需要的上下文

      3)G:协程

    设置运行的cpu数目:

    func main() {
        cupNum := runtime.NumCPU()
        fmt.Println(cupNum)
    
        // 设置运行的cpu数目
        runtime.GOMAXPROCS(cupNum)
    }

     channel

      1)channel本质就是一个数据结构-队列

      2)数据是先进先出

      3)线程安全,多goroutine访问时,不需要加锁,就是说channel本身就是线程安全的

      4)channel有类型的,一个string的channel只能存放string数据

      5)示意图:

      

       定义声明channel

        var 变量名 chan 数据类型

        举例:

          var intChan chan int

          var mapChan chan map[int]string

          var perChan chan Person

          var perChan2 chan *Person

        1)channel是引用类型

        2)channel必须初始化才能写入数据,即make后才能使用

        3)管道是有类型的,intChan只能写入整数int

      管道的初始化,写入与读取数据及基本的注意事项  

    func main() {
        var intChan chan int
        intChan = make(chan int, 4)
    
        fmt.Printf("intChan 的值=%v inChan本身的地址=%p
    ", intChan, &intChan)
    
        // 存数据
        intChan <- 10
        num := 23
        intChan <- num
        intChan <- 30
        intChan<- 22
        fmt.Printf("channel len=%v cap=%v 
    ", len(intChan), cap(intChan))
    
        // 读取数据
        var num2 int
        num2 = <-intChan
        fmt.Println("num2=",num2)
        fmt.Printf("channel len=%v cap=%v 
    ", len(intChan), cap(intChan))
    }

     channel的关闭

      使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。

    channel的遍历

      channel支持for-range的方式进行遍历,请注意两个细节

        1)在遍历时,如果channel没有关闭,则会出现deadlock的错误

        2)在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。

    goroutine和channel结合使用案例

    func writeData(intChan chan int) {
        for i := 0; i < 50; i++ {
            intChan<- i
            fmt.Println("writeData ", i)
        }
        close(intChan)
    }
    
    func readData(intChan chan int, exitChan chan bool) {
        for {
            v, ok := <-intChan
            if !ok {
                break
            }
            fmt.Println("readData ", v)
        }
        exitChan<- true
        close(exitChan)
    }
    
    func main() {
        intChan := make(chan int, 50)
        exitChan := make(chan bool, 1)
    
        go writeData(intChan)
        go readData(intChan, exitChan)
        for {
            _, ok := <-exitChan
            if !ok {
                break
            }
        }
    }

     channel阻塞

      如果只是向管道写入数据,而没有读取,就会出现阻塞而dead lock,原因是容量不足

      如果,编译器运行,发现一个管道只有写,而没有读,则该管道会阻塞

     channel注意事项

      1)channel可以声明为只读(var chan2 chan<- int),或者只写(var chan2 <-chan int)性质

      2)使用select可以解决从管道取数据的阻塞问题

      3)goroutine中使用recover,解决协程中出现panic,导致程序崩溃问题

        如果我们起了一个协程,但是这个协程出现了panic,如果我们没有捕获这个panic,就会造成整个程序崩溃,这时我们可以在goroutine中使用recover来捕获panic进行处理,这样即使这个协程发生的问题,但是主线程仍然不受影响,可以继续执行。

    func sayHello() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Second)
            fmt.Println("hello world!")
        }
    }
    
    func test() {
        defer func() {
            if err := recover(); err != nil {
                fmt.Println("test() 发生了错误!")
            }
        }()
        var myMap map[int]string
        myMap[1] = "golang"
    }
    
    func main() {
        go sayHello()
        go test()
    
        for i := 0 ;i < 10; i++ {
            fmt.Println("main() ok=", i)
            time.Sleep(time.Second)
        }
    }
    人生就是要不断折腾
  • 相关阅读:
    MongoDB Query 的几个方法
    jQuery日期和时间插件(jqueryuitimepickeraddon.js)中文破解版使用
    entity framework使用技巧
    SQL Server TSQL高级查询
    Visual Studio 2012资源管理器里单击打开改为双击打开文件
    ASP.NET MVC 3发布报错(ASP.NET MVC 3在没有安装环境的服务器上运行)的解决方案
    排序算法时间测试比较
    读书笔记之:C++ STL 开发技术导引3
    如何判断整数x的二进制中含有多少个1
    面试题:2012民生银行总行笔试题
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/12258022.html
Copyright © 2020-2023  润新知