1
在操作系统中,有两个重要的概念:一个是进程、一个是线程。当我们运行一个程序的时候,比如你的IDE或者QQ等,操作系统会为这个程序创建一个进程,这个进程包含了运行这个程序所需的各种资源,可以说它是一个容器,是属于这个程序的工作空间,比如它里面有内存空间、文件句柄、设备和线程等等。
那么线程是什么呢?线程是一个执行的空间,比如要下载一个文件,访问一次网络等等。线程会被操作系统调用,来在不同的处理器上运行编写的代码任务,这个处理器不一定是该程序进程所在的处理。操作系统过的调度是操作系统负责的,不同的操作系统可能会不一样,但是对于我们程序编写者来说,不用关心,因为对我们都是透明的。
一个进程在启动的时候,会创建一个主线程,这个主线程结束的时候,程序进程也就终止了,所以一个进程至少有一个线程,这也是我们在main函数里,使用goroutine的时候,要让主线程等待的原因,因为主线程结束了,程序就终止了,那么就有可能会看不到goroutine的输出。
go语言中并发指的是让某个函数独立于其他函数运行的能力,一个goroutine就是一个独立的工作单元,Go的runtime(运行时)会在逻辑处理器上调度这些goroutine来运行,一个逻辑处理器绑定一个操作系统线程,所以说goroutine不是线程,它是一个协程,也是这个原因,它是由Go语言运行时本身的算法实现的。
这里我们总结下几个概念:
概念 说明 进程 一个程序对应一个独立程序空间 线程 一个执行空间,一个进程可以有多个线程 逻辑处理器 执行创建的goroutine,绑定一个线程 调度器 Go运行时中的,分配goroutine给不同的逻辑处理器 全局运行队列 所有刚创建的goroutine都会放到这里 本地运行队列 逻辑处理器的goroutine队列 当我们创建一个goroutine的后,会先存放在全局运行队列中,等待Go运行时的调度器进行调度,把他们分配给其中的一个逻辑处理器,并放到这个逻辑处理器对应的本地运行队列中,最终等着被逻辑处理器执行即可。
2
协程(Coroutine)是在1963年由Melvin E. Conway USAF, Bedford, MA等人提出的一个概念。而且协程的概念是早于线程(Thread)提出的。但是由于协程是非抢占式的调度,无法实现公平的任务调用。也无法直接利用多核优势。因此,我们不能武断地说协程是比线程更高级的技术。 尽管,在任务调度上,协程是弱于线程的。但是在资源消耗上,协程则是极低的。一个线程的内存在MB级别,而协程只需要KB级别。而且线程的调度需要内核态与用户的频繁切入切出,资源消耗也不小。 我们把协程的基本特点归纳为:
- 协程调度机制无法实现公平调度
- 协程的资源开销是非常低的,一台普通的服务器就可以支持百万协程。 那么,近几年为何协程的概念可以大热。我认为一个特殊的场景使得协程能够广泛的发挥其优势,并且屏蔽掉了劣势 — 网络编程。 与一般的计算机程序相比,网络编程有其独有的特点。
- 高并发(每秒钟上千数万的单机访问量)
- Request/Response。程序生命期端(毫秒,秒级)
- 高IO,低计算(连接数据库,请求API)。
最开始的网络程序其实就是一个线程一个请求设计的(Apache)。后来,随着网络的普及,诞生了C10K问题。Nginx 通过单线程异步IO把网络程序的执行流程进行了乱序化,通过IO事件机制最大化的保证了CPU的利用率。
至此,现代网络程序的架构已经形成。基于IO事件调度的异步编程。其代表作恐怕就属NodeJS了吧。
异步编程的槽点
异步编程为了追求程序的性能,强行的将线性的程序打乱,程序变得非常的混乱与复杂。对程序状态的管理也变得异常困难。
3
Goroutines优于线程的优势 1.与线程相比,Goroutines非常便宜。它们的堆栈大小只有几kb,堆栈可根据应用程序的需要增长和缩小,而在线程的情况下,堆栈大小必须指定并固定。 2.Goroutines被多路复用到少量的OS线程。一个程序中可能只有一个线程,有数千个Goroutine。如果该线程中的任何Goroutine阻止了等待用户输入,则创建另一个OS线程,并将剩余的Goroutine移动到新的OS线程。所有这些都由运行时处理,我们作为程序员从这些复杂的细节中抽象出来,并给予一个干净的API来处理并发。 3.Goroutines使用渠道进行沟通。使用Goroutine访问共享内存时,通道设计可以防止竞争状况的发生。渠道可以被认为是Goroutines沟通的管道
4
知乎链接:协程图示P M G 概念 https://www.zhihu.com/question/20862617