前面我们提到因为Go语言的高并发、高性能,所以选择Go,这次我们看看Go的高并发。
并发编程
关于并发和并行
- 并发:早期计算机的 CPU 都是单核的,一个 CPU 在同一时间只能执行一个进程/线程,当系统中有多个进程/线程等待执行时,CPU 只能执行完一个再执行下一个。简单来讲,并发就是多线程程序在一个核的CPU上的运行。
- 并行:并行是针对多核CPU提出的,多核CPU真正实现了“同时执行多个任务”。并行即多线程程序在多个核的CPU上的运行。
多线线程在单核cpu上的运行:
多线线程在多核cpu上的运行:
Go就可以发挥多核优势,最大限度调用计算机资源
关于协程-Goroutine
- 线程:内核态,线程跑多个协程,栈MB级别
- 协程:用户态,轻量级线程,栈KB级别
快速打印hello goroutine:0~hello goroutine:4,就要快速开启多个协程
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
如上代码,Go语言要开启协程,在调用函数时,在函数前加上go关键字
CPS模型
如上两张图,第一种是通过通信共享内存,第二种是我们熟悉的通过共享内存来实现通信;Go语言提倡通过通信共享内存,于是,GO语言采用了CPS模式
其中,Channel通道是我们必须了解的
Channel操作:make(chan 元素类型,[缓冲大小])
根据缓冲通道的大小,通道又可以分为:
- 无缓冲通道:发送与接收同步,也被称为同步通道。
- 例如:
make(chan int)
- 有缓冲通道:通道会阻塞发送。
- 例如:
make(chan int,2)
- 缓冲通道就像是货架,是为了照顾消费者速度
- 使用带缓冲的队列,不影响生产者执行效率
举一个例子:
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
//复杂操作
println(i)
}
}
src和dest两个channel输出0-9平方,保证了输出的顺序性,即并发安全。
参考
- c语言中文网