并行与并发
- 并⾏ 在任何时间粒度下,程序都是同时运行的。
- 并发 在规定的时间内,多个程序是同时运行的,看上去两者相差不大,但实际不然,并发是给外界的感觉。就拿单核CPU来说,分时操作系统的设计⽬的就是为了支持并发,给外界的感觉是多个进程在同时运⾏。换句话说,想要⽀持并行,必须得是多核的。
并发和并行都是为了提⾼机器硬件利⽤率,提升应用运行效率。并行和并发就是为达⽬的两个⼿段。并⾏可以提升CPU核⼼数,并发则是通过系统设计,⽐如分时复⽤系统,多进程,多线程的开发⽅法,不过在对并发的⽀持上,Go语⾔是天生最好的,因为它设计的理念就是并发,⽽且是在语⾔层⾯实现的并发。
相⽐与多进程、多线程这样的⽅式,并发则是CPU在多个进程或线程间切换,这个切换成本是蛮⾼的,Go内部实现了go-routine(例程、协程),以后我们统⼀用goroutine。goroutine是比线程更小的运⾏单位,因此Go语言并发时goroutine没有上限的限制,可以随意启动goroutine,成千上万个也没问题。⽽Go语⾔启动goroutine也⾮常容易,启动一个函数就好了了。
go call_func()
goroutine没有父⼦之分,开发者也不要预估goroutine们的执行先后次序,Go语⾔的运行时(runtime)为main函数分配一个main-goroutine,如果程序内有go关键字,那么会再启动其他goroutine,在goroutine内部当然也可以继续启动多个 goroutine。
- G代表goroutine,存储goroutine的执行栈信息、 状态及任务函数等。
- P是一个“逻辑处理器”。每个G要想真正运行起来,首先需要被分配一个P。
- 对于G来说,P就是运行它的“CPU”,可以说在G的眼里只有P。
- M代表真正的执行计算资源,只有将P和M绑定才能让P本地队列中的G真正运行起来。
- M在绑定有效的P后,进入一个调度循环。
- M并不保留G状态,这是G可以跨M调度的基础。
代码案例:
执行结果:
配置goroutine: