浅谈go协程及其调度模型

每当我看着大海的时候,我总想找人谈谈。但当我和人交谈时,我又总想去看看大海。                                                                                                     —— 村上春树

go在语言层面对协程进行了原生的支持并且称为goroutine,这也是go语言强大并发能力的重要支撑。

本文并非一蹴而就,而是蓄势待发已久。后续随着知识及理解的不断深入仍将持续补充进来。同时也欢迎各位留言探讨,互相学习。

目录

进程、线程、协程

为什么会选择协程

goroutine调度模型-GPM

并发与并行


进程、线程、协程

1,进程是系统资源分配的最小单位,线程是CPU调度的最小单位;

2,进程是线程的载体,一个进程可由多个线程组成,进程内的多个线程间可以相互通信;

3,进程的创建和销毁都是系统资源级别,因此是一种比较昂贵的操作;

4,线程是抢占式调度,协程是协作式调度,多线程无可避免的会带来频繁的CPU上下文切换,调度成本高,但抢占式调度由系统内核来完成的,用户态不需要参与,内核参与使得平台移植好;协作式调度中用户态协程会主动让出CPU控制权来让其他协程使用;

5,协程不和内核交互,协程是用户态轻量级线程;

6,线程与协程可简单的理解为捆绑关系,任意数量的用户态协程可以运行在任意数量的os线程上;用户创建的协程会被专门负责管理goroutine的processor接管调度,一个processor可对应N个协程;

7,原生的linux线程从进程栈分配空间,所分配的线程栈空间大小默认为8M:

而创建一个协程分配的空间是2k,空间可能会自动扩增。

为什么会选择协程

原生线程的调度成本高,那么go语言自己进行了接管调度---go runtime进行调度。

goroutine调度模型-GPM

源码位置:src/runtime/proc.go

// Goroutine scheduler
// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
//
// The main concepts are:
// G - goroutine.
// M - worker thread, or machine.
// P - processor, a resource that is required to execute Go code.
//     M must have an associated P to execute Go code, however it can be
//     blocked or in a syscall w/o an associated P.

G:goroutine
M:系统内核线程
P:执行go代码所需的处理器,是负责golang运行时的线程,负责执行goroutine

M必须具有关联的P才能调度goroutine,即每个P对应着一个M。新创建的goroutine会先存放在待调度队列(图中灰色部分的G所在队列)中,等待调度器进行调度。

M与P绑定后,M先从P的本地运行队列(图中浅蓝色部分的G所在的待运行队列)中取出G,并切换到G的堆栈执行,当P的本地队列中没有G时,再从待调度队列中获取一个G分配给这个P,当待调度队列中也没有待运行的G时,则尝试从其它的P窃取部分G来执行。

通过这种模型,go避免了大量的上下文切换。

并发与并行

文末,顺带聊聊并发与并行

并行:你在吃饭,来了个电话,无关紧要,你一边吃一边说;

并发:你在吃饭,来了个电话,因为一些因素必须通完电话才能继续吃饭(你的身体先由打电话支配使用),通完电话后继续吃饭(身体切换到可以吃饭)。

发布了172 篇原创文章 · 获赞 114 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/HYZX_9987/article/details/104397128