golang 并发基础(一)

golang 并发基础(一)

golang 在语言的语法和运行时方面内置了对并发的支持。在了解golang的并发之前,我们先看一下操作系统的两个概念:线程和进程。你应该知道,在系统中运行一个程序,其实就是启动了一个进程,而要运行起来,对外来说肯定是需要资源的,比如cpu,内存空间,文件句柄,线程等等..每个进程至少有一个线程,每个进程的初始线程被称为主线程。了解到这些,你大概可以类比一下,比如你创建一个项目,肯定要有main方法(主线程),你的项目中可能还有不同的业务逻辑,这个时候就可以让主进程创建多个线程去运行求结果。线程可以共享进程的空间。另外我们要知道,cpu的执行方式是轮询时间分片的,也就是可能这个线程有10ms的cpu执行时间,所以如果是单核cpu,就是每个线程执行一会,抢占式执行。并发(concurrency)不是并行(parallelism)。并行是在相同的时候让不同的代码都能在不同的处理器上执行,也就是同时做很多事情。并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。

并发通信模型

golang 采用的并发同步模型为通信顺序进程(Communicating Sequential Process,CSP)的模型,通过在goroutine之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问。并发通信模型还有一种是Actor,比较著名的例子就是akka.io。

golang 并发工作模式

操作系统会在物理处理器上调度线程来运行,而Go语言的运行时会在逻辑处理器上调度goroutine来执行,每个逻辑处理器又分别绑定到单个的操作系统线程。像下图,如果一个Goroutine可以运行的时候,就会被放入到逻辑处理器的执行队列中,逻辑处理器有绑定到一个唯一的操作系统线程中。

2019-08-25-20-03-52

如果一个goroutine执行的是一个阻塞的系统调用,比如打开文件,那岂不是队列中的就绪态goroutine都的阻塞了?当出现这类调用时候,当前线程和当前的goroutine就会从逻辑处理器上分离,当前线程继续阻塞,等待刚刚的系统调用的放回。同时,逻辑处理器失去用来运行的线程。所以调度器就会创建一个新线程,并将其绑定到逻辑处理器上。当阻塞的系统完成调用并且返回,对应的goroutine会放回到本地运行队列,而之前的线程会保存好,一遍之后可以继续使用。如下图,线程A本来是绑定在P0的,后来有阻塞调用,就脱离出去了,然后线程B又绑定了P0。

2019-08-25-20-21-14

golang默认可以创建的线程数量为10000个线程,可以通过runtime/debug包的SetMaxThreads方法来更改。

参考:

The C10K problem

https://cloud.tencent.com/developer/article/1346210

本文标题:golang 并发基础(一)

文章作者:陈志军

发布时间:2019-08-25 20:40:13

原始链接:http://chenzhijun.me/2019/08/25/golang-goroutine1/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!