一、Go语言中Goroutine的基本原理
Go语言里的并发指的是能让某个函数独立于其他函数运行的能力。
Go语言的goroutine是一个独立的工作单元,
Go 语言的并发同步模型来自一个叫作通信顺序进程(Communicating Sequential Processes,CSP)的范型(paradigm)。
CSP 是一种消息传递模型,通过在goroutine 之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问。消息的传递通过Go语言中的Channel(通道)来实现。
进程(process) 看作一个包含了应用程序在运行中需要用到和维护的各种资源的容器。
线程(Thread)是一个执行空间,这个空间会被操作系统调度来运行函数中所写的代码。
每个进程至少包含一个线程,每个进程的初始线程被称作主线程。因为执行这个线程的空间是应用程序的本身的空间,所以当主线程终止时,应用程序也会终止。
下图就是进程和线程的简要关系。
补充:句柄是什么?
我的理解句柄就是给用户操作内核资源的指针,指向指针的指针。【把手】和【门】的关系,使用们把手能转动整个门(系统资源)
操作系统会在物理处理器上调度线程来运行,而Go 语言的运行时会在逻辑处理器上调度goroutine来运行。每个逻辑处理器都分别绑定到单个操作系统线程。下面我们就来简单描述一下goroutine执行的流程。
首先来了解一下操作系统线程、逻辑处理器和本地运行队列
全局运行队列:刚创建的goruntine会被安排到这里面,通过一些调度算法,分配给逻辑处理器
操作系统线程:这个就是传统意义上的线程,用于具体goroutine的执行,他会和一个逻辑器进行绑定
逻辑处理器:里面维护着一个本地的队列,用于本地队列里面goroutine的调度
本地运行队列:装载着待执行的goruntine
支撑整个调度器的主要有4个重要结构,分别是M、G、P、Sched,前三个定义在runtime.h中,Sched定义在proc.c中。
- Sched结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。
- M代表内核级线程,一个M就是一个线程,goroutine就是跑在M之上的;M是一个很大的结构,里面维护小对象内存cache(mcache)、当前执行的goroutine、随机数发生器等等非常多的信息。
- P全称是Processor,处理器,它的主要用途就是用来执行goroutine的,所以它也维护了一个goroutine队列,里面存储了所有需要它来执行的goroutine,这个P的角色可能有一点让人迷惑,一开始容易和M冲突,后面重点聊一下它们的关系。
- G就是goroutine实现的核心结构了,G维护了goroutine需要的栈、程序计数器以及它所在的M等信息。