go-并发剖析

2020-03-02  本文已影响0人  GGBond_8488

进程、线程、协程

进程:进程是系统进行资源分配的基本单元,有独立的内存空间
线程:线程是cpu调度和分派的基本单位,线程依附于进程存在,每个线程共享父进程的资源
协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制,协程切换只需要保存任务的上下文,没有内核的开销

协程与多线程相比,其优势体现在:协程的执行效率极高。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

线程上下文切换

由于中断处理,多任务处理,用户态切换等等原因会导致CPU从一个线程切换到另一个线程,切换过程中要保存一个线程的状态切换到另一个线程的状态。
而这个上下文切换代价极高
为了解决传统的多线程问题Golang引入了Goroutine
Goroutine非常轻量

Go并发调度的GMP模型

在操作系统提供的内核线程之上,Go搭建了一个特有的两级线程模型。goroutine机制实现了M : N的线程模型
,goroutine机制是协程(coroutine)的一种实现,golang内置的调度器,可以让多核CPU中每个CPU执行一个协程。

Go的调度器是如何工作的

Go语言中支撑整个scheduler实现的主要有4个重要结构,分别是M、G、P、Sched, 前三个定义在runtime.h中,Sched定义在proc.c中。

Processor的数量是在启动时被设置为环境变量GOMAXPROCS的值,或者通过运行时调用函数GOMAXPROCS()进行设置。Processor数量固定意味着任意时刻只有GOMAXPROCS个线程在运行go代码。

我们分别用三角形,矩形和圆形表示Machine Processor和Goroutine。

GMP

在单核处理器的场景下,所有goroutine运行在同一个M系统线程中,每一个M系统线程维护一个Processor,任何时刻,一个Processor中只有一个goroutine,其他goroutine在runqueue中等待。一个goroutine运行完自己的时间片后,让出上下文,回到runqueue中。 多核处理器的场景下,为了运行goroutines,每个M系统线程会持有一个Processor。


单核

Go运行时系统中的调度器的主要职责就是将G公平合理的安排到多个M上去执行
在正常情况下,scheduler会按照上面的流程进行调度,但是为了充分利用线程的计算资源,Go调度器采取以下几种策略

任务窃取

为了提高Go并行处理能力,提高整体效率,当每个p之间的G任务不均衡时,调度器允许从GRQ(全局运行队列)LRQ(本地运行队列)中获取G


任务窃取
减少阻塞

当正在运行的goroutine阻塞的时候,例如进行系统调用,会再创建一个系统线程(M1),当前的M线程放弃了它的Processor,P转到新的线程中去运行


阻塞
上一篇 下一篇

猜你喜欢

热点阅读