目录

Go-goroutine协程

进程和线程

  • 进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位
  • 线程是进程的一个执行实例,是程序执行的最小单位,它是比进程更小的能独立运行的基本单位
  • 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行
  • 一个程序至少有一个进程,一个进程至少有一个线程

并发和并行

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

并发

因为是在一个CPU上,比如有10个线程,每个线程执行10毫秒(进行轮训操作),从人的角度看,好像这10个线程都在运行,但是从微观上看,在某一时间点看,其实只有一个线程在执行,这就是并发

并行

因为是在多个CPU上(比如有10个CPU),比如有10个线程,每个线程执行10毫秒(各自在不同CPU上执行),从人的角度看,这10个线程都在运行,但是从微观上看,在某一个时间点看,也同时有10个线程在执行,这就是并行

Go协程和主线程

Go主线程

一个Go线程上,可以起多个协程,可以理解为,协程是轻量级的线程(编译器做的优化)

Go协程的特点

  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级的线程

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 每隔1秒输出
func test() {
	for i := 1; i <= 5; i++ {
		fmt.Println("Hello,World" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main() {

	go test() //开启了一个协程

	for i := 1; i <= 5; i++ {
		fmt.Println("(main)Hello,World" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}

}

结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(main)Hello,World1
Hello,World1
Hello,World2
(main)Hello,World2
(main)Hello,World3
Hello,World3
(main)Hello,World4
Hello,World4
Hello,World5
(main)Hello,World5

执行流程图

https://raw.githubusercontent.com/XD825/picgo/main/img/202207220845216.png

结论

  1. 主线程是一个物理线程,直接作用在CPU上,是重量级的,非常耗费CPU资源。
  2. 协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小。
  3. Go的协程机制是重要的特点,可以轻松的开启上万个协程。

MPG模式

MPG模式基本介绍

https://raw.githubusercontent.com/XD825/picgo/main/img/202207220851527.png

  • M: 操作系统的主线程(物理线程)
  • P: 协程执行需要的上下文
  • G: 协程

MPG模式运行的状态1

https://raw.githubusercontent.com/XD825/picgo/main/img/202207220900720.png

  1. 当前程序有三个M,如果三个M都在一个CPU运行,就是并发,如果在不同的CPU运行就是并行
  2. M1、M2、M3正在执行一个G,M1的协程队列有3个,M2的协程队列有3个,M3协程队列有2个
  3. 从上图可以看到:Go的协程是轻量级的线程,是逻辑态的,Go可以容易的起上万个协程

MPG模式运行的状态2

https://raw.githubusercontent.com/XD825/picgo/main/img/202207220907180.png

  1. 分两部分看
  2. 原来的情况是M0主线程正在执行G0协程,另外有三个协程在队列等待
  3. 如果G0协程阻塞,比如读取文件或者数据库操作等
  4. 这时就会创建M1主线程(也可能是从已有的线程池中取出M1),并且将等待的3个协程挂到M1下开始执行,M0的主线程下的G0任然继续执行
  5. 这样的MPG调度模式,既可以让G0执行,同样也不会让队列的其他协程一直阻塞,仍然可以并发/并行执行
  6. 等到G0不阻塞的,M0会被放到空闲的主线程继续执行(从已有的线程池中取),同时G0又会被唤醒

设置Go运行的CPU数

为了充分利用CPU优势,可以在Golang程序中,设置运行的CPU数目

1
2
3
4
5
// 获取当前系统的CPU数量
num := runtime.NumCPU()
// 设置运行go程序可同时执行的最大CPU数
runtime.GOMAXPROCS(num)
fmt.Println("当前系统CPU数量:", num) //当前系统CPU数量: 8
  1. go1.8后,默认程序运行在多个核上,可以不用设置
  2. go1.8前,需要设置,可以更高效的利用CPU