并发 一个 CPU 上能同时执行多项任务,在很短时间内,CPU 来回切换任务执行(在某段很短时间内执行 程序 a,然后又迅速得切换到程序 b 去执行),有时间上的重叠(宏观上是同时的,微观仍是顺序执行),这样看起来多个任务像是同时执行,这就是并发。
并行 当系统有多个 CPU 时,每个 CPU 同一时刻都运行任务,互不抢占自己所在的 CPU 资源,同时进行, 称为并行。
进程 CPU 在切换程序的时候,如果不保存上一个程序的状态(context --上下文),直接切换下一个程 序,就会丢失上一个程序的一系列状态,于是引入了进程这个概念,用以划分好程序运行时所需 要的资源。因此进程就是一个程序运行时候的所需要的基本资源单位(也可以说是程序运行的一 个实体)。
线程 CPU 切换多个进程的时候,会花费不少的时间,因为切换进程需要切换到内核态,而每次调度需 要内核态都需要读取用户态的数据,进程一旦多起来,CPU 调度会消耗一大堆资源,因此引入了 线程的概念,线程本身几乎不占有资源,他们共享进程里的资源,内核调度起来不会那么像进程 切换那么耗费资源。协程 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在 切回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态(即 所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说 法:进入上一次离开时所处逻辑流的位置。线程和进程的操作是由程序触发系统接口,最后的执 行者是系统;协程的操作执行者则是用户自身程序,goroutine 也是协程。
生产者消费者模型
package main
import (
"fmt"
"time"
)
// 生产者: 生成 factor 整数倍的序列
func Producer(factor int, out chan<- int) {
for i := 0; ; i++ {
out <- i * factor
}
}
// 消费者
func Consumer(in <-chan int) {
for v := range in {
fmt.Println(v)
}
}
func main() {
ch := make(chan int, 64) // 成果队列
go Producer(3, ch) // 生成 3 的倍数的序列
go Producer(5, ch) // 生成 5 的倍数的序列
go Consumer(ch) // 消费 生成的队列
// 运行一定时间后退出
time.Sleep(5 * time.Second)
}
Go 语言的并发处理参考了 CSP(Communicating Sequential Process 通讯顺序进程)模型。CSP 并发模型是 Hoare 在 1978 年提出的 CSP 的概念,不同于传统的多线程通过共享内存来通信, CSP 有着精确的数学模型,并实际应用在了 Hoare 参与设计的 T9000 通用计算机上。CSP 讲究的是“以通信的方式来共享内存”。
Don’t communicate by sharing memory; instead, share memory by communicating. 不要通过共享内存来通信,而应通过通信来共享内存。
Go 的 CSP 模型实现与原始的 CSP 实现有点差别:原始的 CSP 中 channel 里的任务都是立即执行的, 而 go 语言为其增加了一个缓存,即任务可以先暂存起来,等待执行线程准备好再顺序执行。
Go 的 CSP 并发模型,是通过 goroutine 和 channel 来实现的。
生成一个 goroutine 的方式非常的简单:Go 一下,就生成了。
gof()
通信机制 channel 也很方便,传数据用 channel <- data,取数据用<-channel。在通信过程中,传数据 channel <- data 和取数据<-channel 必然会成对出现,因为这边传,那边 取,两个 goroutine 之间才会实现通信。而且不管传还是取,必阻塞,直到另外的 goroutine 传或者取为止。
Go 语言运行时环境提供了非常强大的管理 goroutine 和系统内核线程的调度器, 内部提供了三种 对象:Goroutine,Machine,Processor。
Goroutine : 指应用创建的 goroutine
Machine : 指系统内核线程。
Processor : 指承载多个 goroutine 的运行器
在宏观上说,Goroutine 与 Machine 因为 Processor 的存在,形成了多对多(M:N)的关系。M个 用户线程对应N个系统线程,缺点增加了调度器的实现难度.
Goroutine 是 Go 语言中并发的执行单位。Goroutine 底层是使用协程 (coroutine) 实现,coroutine 是一种运行在用户态的用户线程(参考操作系统原理:内核态,用户态)它可以由语言和框架层 调度。Go 在语言层面实现了调度器,同时对网络,IO 库进行了封装处理,屏蔽了操作系统层面的 复杂的细节,在语言层面提供统一的关键字支持。
三者与内核级线程的关系如下图所示:
一个 Machine 会对应一个内核线程(K),同时会有一个 Processor 与它绑定。一个 Processor 连 接一个或者多个Goroutine。Processor有一个运行时的Goroutine(上图中绿色的G),其它的 Goroutine 处于等待状态。
Processor 的数量同时可以并发任务的数量,可通过 GOMAXPROCS 限制同时执行用户级任务的操 作系统线程。GOMAXPROCS 值默认是 CPU 的可用核心数,但是其数量是可以指定的。在 go 语言 运行时环境,可以使用
runtime.GOMAXPROCS(MaxProcs)
来指定 Processor 数量。
func schedinit() {
//设置最大的M数量
sched.maxmcount = 10000
}
51Reboot 2.13号分享《Goroutine 原理解析》
内容:
1、goroutine 原理解析
2、channel 原理 channel 数据结构
地址:https://ke.qq.com/course/860855?taid=5461862666543799&tuin=31589b0e