说说Golang goroutine并发那些事儿 (3)

阻塞状态指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续处理其他的事情,则称该程序在该操作上是阻塞的。

常见的阻塞形式有:网络 I/O 阻塞、磁盘 I/O 阻塞、用户输入阻塞等。阻塞是无处不在的,包括 CPU 切换上下文时,所有的进程都无法真正处理事情,它们也会被阻塞。如果是多核 CPU 则正在执行上下文切换操作的核不可被利用。

非阻塞

程序在等待某操作过程中,自身不被阻塞,可以继续处理其他的事情,则称该程序在该操作上是非阻塞的。

非阻塞并不是在任何程序级别、任何情况下都可以存在的。仅当程序封装的级别可以囊括独立的子程序单元时,它才可能存在非阻塞状态。

非阻塞的存在是因为阻塞存在,正因为某个操作阻塞导致的耗时与效率低下,我们才要把它变成非阻塞的。

同步与异步 同步

不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,我们称这些程序单元是同步执行的。

例如购物系统中更新商品库存,需要用“行锁”作为通信信号,让不同的更新请求强制排队顺序执行,那更新库存的操作是同步的。

简言之,同步意味着有序。

异步

为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。

例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确定。

可异步与不可异步

经过以上了解,又是进程、又是线程、等等一系列的东西,那是真的难受。不过相信你已经有个初步的概率,那么这里我们将更加深入的去了解可异步与不可异步。

在此之前先总结一下,以上各种演进的路线,其实加速无非就是一句话,提高效率。(废话~)

那么提高效率的是两大因素,增加投入以求增加产出、尽可能避免不必要的损耗(例如:减少上下文切换等等)。

如何区分它是可异步代码还是不可异步呢,其实很简单那就是,它是否能够自主完成不需要我们参与的部分。

我们从结果反向思考,

例如我们发送一个网络请求,这之间拥有网络I/O阻塞,那么测试我们将它挂起、转而去做其他事情,等他响应了,我们在进行此阶段的下一步的操作。那么这个是可异步的

另外:写作业与上洗手间,我此时正在写着作业,突然,我想上洗手间了,走。上完洗手间后又回来继续写作业,在我去洗手间这段时间作业是不会有任何进展,所以我们可以理解为这是非异步

goroutine

东扯一句,西扯一句,终于该上真家伙了,废话不多说。

如何实现只需定义很多个任务,让系统去帮助我们把这些任务分配到CPU上实现并发执行。

Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。

在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了

goroutine与线程 可增长的栈

OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这么大。所以在Go语言中一次创建十万左右的goroutine也是可以的。

goroutine模型

GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。

G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspwzj.html