主要参考以上4篇文章的一些小结。
一、进程
进程可看作为分配资源的基本单位,比如打开一个应用程序或一个窗体,分配完整且独立的内存空间(有独立的堆和栈),进程的切换由操作系统来调度
可以简单的理解为下图的程序
chrome.exe *32 每打开一个网页都会分配一个进程和独立的内存空间
devenv.exe 这个就是visual studio打开的实例
DocToPDF.exe 有道笔记的doc转pdf文档
....
二、线程
我们可以认为线程是进程的一个执行流,独立执行它自己的程序代码。线程上下文一般只包含CPU上下文及其他的线程管理信息,线程创建的开销主要取决于为线程堆栈的建立而分配内存的开销,这些开销并不大。线程还分为系统级别和用户级线程,用户级别线程对引起阻塞的系统调用的调用会立即阻塞该线程所属的整个进程,而内核实现线程则会导致线程上下文切换的开销跟进程一样大,所以经常的折衷的方法是轻量级进程(Lightweight)。在 Linux 中,一个线程组基本上就是实现了多线程应用的一组轻量级进程。线程的作用就在于充分使用硬件CPU,也就是我们说的并行。
三、协程
协程是轻量级的线程?? 感觉应该不是
协程与子例程一样,协程(coroutine)也是一种程序组件,或者说是一个特定的函数。goroutine可以看作是协程的go语言实现,它是语言原生支持的,相对于一般由库实现协程的方式,goroutine更加强大,它的调度一定程度上是由go运行时(runtime)管理。其好处之一是,当某goroutine发生阻塞时(例如同步IO操作等),会自动出让CPU给其它goroutine。
三者之前的关系,画了个图(可能图也不一定对,只是想画个图看起来清楚一些)
四、goroutine的调度
goroutine调度器主要有4个重要结构:
- Sched:它的结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等
- M:代表内核级线程,一个M就是一个线程,goroutine就是跑在M之上的;M是一个很大的结构,里面维护小对象内存cache(mcache)、当前执行的goroutine、随机数发生器等等非常多的信息
- P:全称是Processor,处理器,它的主要用途就是用来执行goroutine的,所以它也维护了一个goroutine队列,里面存储了所有需要它来执行的goroutine
- G:goroutine实现的核心结构,G维护了goroutine需要的栈、程序计数器以及它所在的M等信息。
M代表OS线程,G代表goroutine,P的概念比较重要,它表示执行的上下文,其数量由$GOMAXPROCS决定,一般来说正好等于处理器的数量。M必须和P绑定才能执行G,调度器需要保证所有的P都有G执行,以保证并行度。如下图: