一.进程、线程、协程介绍
进程:系统中所有的应用程序都是以进程(process)的方式运行,是系统进行资源分配和调度的基本单位,每个进程都有自己的独立的地址空间,使得进程之间的地址空间相互隔离。
线程:线程是程序执行流的最小单元上,通常意义上,一个进程由一个到多个线程组成,各个线程之间共享程序的内存空间(包括代码段、数据段、堆等)及一些进程级的资源(如打开的文件和信号)。
协程:协程在Go语言中,由轻量级线程实现,由Go运行时(runtime)管理。
这里特别说明下并发和并行:
- 并发:多线程程序在单核上运行
- 并行:多线程程序在多核上运行
二.协程与进程、线程的区别
1)进程拥有自己的堆栈,不共享堆和栈,是由操作系统进行调度的。
2)线程拥有自己的独立的栈和共享的堆,也是由操作系统进行调度。
3)协程共享堆,不共享栈,协程的调度由用户控制。
三.协程的优点
1)代码编辑简单,可以将异步处理逻辑代码用同步的方式编写,将多个异步操作集中到一个函数中完成。
2)单线程模式,没有线程安全的问题,不需要加锁操作。
3)性能好,协程是用户态线程,切换更加高效。
四.协程比线程轻量分析
一旦我们创建完线程,就无法决定它什么时候获得时间片,什么时候让出时间片,这里都交给了内核。和我们编写协程时可以控制,可控的切换时机和很小的切换代价,从操作系统有没有调度权来看,协程就是因为不需要进行内核态的切换。
1)Go协程调用跟切换比线程效率高
线程并发执行流程:
线程是内核对外提供的服务,应用程序可以通过系统调用让内核启动线程,由内核来负责线程调度和切换,线程在等待IO操作时标为unrunnable状态会触发上下文切换。现代操作系统一般采用抢占式调度,上下文切换一般发生在时钟中断和系统调用返回前,调度器计算当前线程的时间片,如果需要切换就从运行队列中选出一个目标线程,保存当前线程的环境,并且恢复目标线程的运行环境,最典型的就是切换ESP指向目标线程内核堆栈,将EIP指向目标线程上次被调度出时的指令地址。
Go协程并发执行流程:
不依赖操作系统和其提供的线程,Golang自己实现的CSP并发模型实现:M,P,G
Go协程也叫用户态线程,协程之间的切换发生在用户态,在用户态没有时钟中断,系统调用等机制,因此效率高。
2)Go协程占用内存小
执行Go协程只需要极少的栈内存(大概4~5KB),默认情况下,线程栈的大小为1MB。
Goroutine就是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈,所以它非常廉价,我们可以很轻松的创建上万个Goroutine,但它们并不是被操作系统所调度执行。