zoukankan      html  css  js  c++  java
  • go语言goroutine

    Go语言goroutine

    在别的语言里想要在一个程序中实现多任务,如python,python实现多任务可以使用多进程、多线程、携程。但多进程占用资源,多线程无法发挥多核的优势(GIL),python的协程是单线程的,必须等一个任务作出让步,另一个任务才能执行,如果其中一个任务阻塞住,让不出cpu来,那么整个程序都会被阻塞住。

    go语言的goroutine(协程)是一个类似于线程的概念,但是它比线程轻量。当开启了多个goroutine后,程序会将每个goroutine分配给每个cpu的核心,可以充分发挥cpu多核的效率。go的协程效率比为m(cpu核数)/n(goroutine数),而python的协程的效率比为1/n

    开启goroutine

    只需要在将任务封装成一个函数,使用go关键字,就可以开启一个goroutine完成多任务。同时,程序的主函数也是一个goroutine

    package main
    
    import "fmt"
    
    func work() {
    	fmt.Printf("work goroutine")
    }
    
    func main() {
    	go work()
    	fmt.Println("main goroutine")
    }
    

    sync.WaitGroup

    上面的程序启动goroutine是没问题,但是编译运行后会发现只有打印了main goroutine。这是因为,开启在主goroutine里面启动另外一个goroutine后,另外开启的goroutine还没来得及运行,主goroutine就已经结束了,主goroutine结束,由主goroutine中开启的goroutine全部都会结束,所有主goroutine想要其他的goroutine执行,必须得等待。

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func work() {
    	fmt.Println("work")
    }
    
    func main() {
    	go work()
    	fmt.Println("main goroutine")
    	time.Sleep(time.Second)
    }
    

    使用time.Sleep来是程序休眠,来达到让另外goroutine由充足的时间来运行
    但是当开启了多个goroutine后,我们不知道全部的协程运行完毕需要多少时间,我们就无法估量sleep的时间。sleep的时间可能多了,可能少了,少了就会让能成达不到想要的目的,多了就会让程序多休眠,浪费资源,此时,就需要引入sync模块的工具来解决这个问题

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var wg sync.WaitGroup
    
    func work() {
    	defer wg.Done()
    	fmt.Println("work")
    
    }
    
    func main() {
    	wg.Add(1)
    	go work()
    	fmt.Println("main goroutine")
    	wg.Wait() // 等待所有的协程能全部运行完毕
    }
    
    

    使用sync.WaitGroup,当需要开启一个协程前,往WaitGroup里面加1,在协程结束后,向WaitGroup告知已经完成(可以配合defer 关剪字使用),最后在主goroutine(main函数)的最后使用WaitGroup来等待所有的goroutine运行结束(对应的函数执行结束)

    goroutine和线程的关系

    这里的线程指的是操作系统的线程,线程和goroutine启动占用的资源不一样,线程的栈内存一般为2mb,而goroutine在开始的时候占用的栈内存为2kb,goroutine的栈不是固定的,它可以像go的切片一样扩容,最大限制可以达到一个G,所有在go里面可以很容易开启十万个goroutine。

    • GMP调度
      • G:G代表goroutine,里面存放当前的goroutine的信息和当前goroutine所载的P的绑定等信息
      • M:M是go运行时对操作系统的线程的虚拟。一个goroutine最终都是要放在M上运行的
      • P:P管理者一组goroutine队列,P里面存放着当前的goroutine的上下文,P会对当前管理的goroutine队列做一些调度(某些goroutine可能占用cpu时间过长,P会将这个goroutine暂停,然后去让别的goroutine来执行)。当自己的goroutine全部运行结束了,这个P会去全局的队列里面取,如果全局的里面也没有了,它甚至会去别的P里面取获取goroutine,这样可以实现效率的最大化
      • P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。
  • 相关阅读:
    前端与算法 leetcode 344. 反转字符串
    JavaScript闭包使用姿势指南
    前端与算法 leetcode 48. 旋转图像
    前端与算法 leetcode 36. 有效的数独
    前端与算法 leetcode 1. 两数之和
    前端与算法 leetcode 283. 移动零
    前端与编译原理 用js去运行js代码 js2run
    前端与算法 leetcode 66. 加一
    前端与算法 leetcode 350. 两个数组的交集 II
    前端与算法 leetcode 26. 删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/ivy-blogs/p/12659128.html
Copyright © 2011-2022 走看看