共计 3872 个字符,预计需要花费 10 分钟才能阅读完成。
[TOC]
GO 的并发编程分享
之前咱们分享了网络编程,明天咱们来看看 GO 的并发编程分享,咱们先来看看他是个啥
啥是并发编程呢?
指在一台处理器上 同时 解决多个工作
此处说的同时,可不是同一个工夫一起手拉手做同一件事件
并发 是在同一实体上的多个事件,而这个事件在同一时间距离产生的,同一个时间段,有多个工作执行,可是同一个工夫点,只有一个工作在执行
为啥要有并发编程?
随着互联网的遍及,互联网用户人数原来越多,这对系统的性能带来了微小的挑战。
咱们要通过各种形式来高效利用硬件的性能(压迫),从而进步零碎的性能进而晋升用户体验,晋升团队或者企业的竞争力。
并发是为了解决什么问题?目标是啥?
是 充沛的利用好 处理器的每一个核,以达到最高的解决性能,尽可能的使用好每一块砖
可是因为当初咱们应用的 CPU,内存,IO 三者之间速度不尽相同
咱们为了进步零碎性能,计算机系统会将这三者速度进行均衡,以达到最优的成果,都有如下措施:
- 操作系统减少了 过程 、 线程 ,以 分时复用 CPU,进而 平衡 CPU 与 I/O 设施 的速度差别;
- CPU 减少了 缓存,以平衡与内存的速度差别;
- 编译程序优化 指令执行秩序,使得缓存可能失去更加正当地利用。
说到过程和线程,他们都是干啥的呢,咱们顺带说一下?
- 过程 是程序在操作系统中的一次执行过程
是 零碎进行资源分配和调度的一个 独立单位。
- 线程 是过程的一个 执行实体
是 CPU 调度和分派的根本单位, 它是比过程更小的能独立运行的根本单位。
- 一个过程能够创立和撤销多个线程,并且同一个过程中的多个线程之间能够并发执行。
讲到并发编程不得不说并发和并行有啥区别?是不是总是有小伙伴弄不分明他们到底是啥区别,如同一样,又如同不一样
并发和并行的区别
一言蔽之,区别如下:
并发
多线程程序在一个核的 CPU 上运行
并行
多线程程序在多个核的 CPU 上运行
并发就像多个小伙伴跑接力,同一个工夫点只会有一个小伙伴在跑,相互有影响
并行就像是多个小伙伴同一个终点一起跑,互不烦扰
咱们须要记住一点,再强调一波:
并发不是并行
并发次要由切换工夫片来实现 ” 同时 ” 运行
并行则是间接利用多核实现多线程的运行,
在 GO 能够设置应用核数,以施展多核计算机的能力,不过设置核数都是依赖于硬件的
那么,讲到 GO 的并发编程,就必须上咱们的 配角 ,那就是 协程
协程 goroutine 是啥?
协程是一种程序组件
是由子例程(过程、函数、例程、办法、子程序)的概念泛化而来的
子例程只有一个入口点且只返回一次,而协程容许多个入口点,能够在指定地位挂起和复原执行。
协程和线程别离有啥特点嘞
- 协程
独立的栈空间,共享堆空间,调度由用户本人管制
实质上有点相似于用户级线程,这些用户级线程的调度也是本人实现的。
- 线程
一个线程上能够跑多个协程,协程是轻量级的线程。
GO 高并发的起因是啥?
goroutine
奉行通过通信来共享内存- 每个一个 GO 的实例有
4~5KB
的栈内存占用,并且因为 GO 实现机制而大幅缩小的创立和销毁开销 - Golang 在语言层面上就反对协程
goroutine
GOLANG 并发编程波及哪些知识点呢?
- 根本协程的原理,实现形式,尽管说,GO 中应用协程很不便,能够咱们必须要知其然而值其所以然
- Goroutine 池
- runtime 包的应用
- Channel 通道
- 定时器
- 并发且平安的锁
- 原子操作
- select 多路复用
- 等等 …
Goroutine 的那些事
咱们写 C/C++
的时候,咱们必然也是要实现并发编程
咱们通常须要本人保护一个线程池,并且须要本人去包装一个又一个的工作,同时须要本人去调度线程执行工作并保护上下文切换
且做线程池的时候,咱们须要本人做一个线程治理的角色,灵便动静压缩和扩容
可是能不能有这样一种机制,咱们只须要定义多个工作,让零碎去帮忙咱们把这些任务分配到 CPU 上实现并发执行
GO 外面就正好有这样的机制
goroutine
的概念相似于线程
goroutine
是由 Go 的运行时(runtime)调度和治理的
Go 程序会智能地将 goroutine
中的工作正当地调配给每个 CPU
Go 在语言层面曾经内置了调度和上下文切换的机制
写 GO 比拟爽的一个中央是:
在 GO 外面,你不须要去本人写 过程、线程、协程
咱们能够应用 goroutine 包
如何应用 goroutine?
咱们须要让某个工作并发执行的时候,只须要把这个工作包装成一个函数
专门开启一个 goroutine 协程 去执行这个函数就能够了,GO 一个协程,很不便
一个 goroutine 必然对应一个函数,能够创立多个 goroutine 去执行雷同的函数,只是多个协程都是做同一个事件罢了
咱们先来应用一下协程,再来抛砖引玉,适当的分享一下
启动单个协程
func Hi() {fmt.Println("this is Hi Goroutine!")
}
func main() {Hi()
fmt.Println("main goroutine!")
}
咱们个别调用函数是如上这个样子的,成果如下
this is Hi Goroutine!
main goroutine!
其实咱们调用协程的话,也与上述相似
咱们能够应用 go 前面加上函数名字,来开拓一个协程,专门做函数须要执行的事件
func main() {go Hi() // 启动一个 goroutine 协程 去执行 Hi 函数
fmt.Println("main goroutine!")
实际效果咱们能够看到,程序只打印了 main goroutine!
main goroutine!
在程序启动的时候,Go 程序就会为 main() 函数创立一个默认的 goroutine 协程
当 main() 函数返回的时候,刚开拓的另外一个 goroutine 协程 就完结了
所有在 main() 函数中启动的 goroutine 协程 会一起完结,老大死了,其余的傀儡也灰飞烟灭了
咱们也能够让主协程等等肯定子协程,待子协程解决完本人的事件,退出后,主协程再本人退出,这和咱们写 C/C++ 的过程 和 线程 的时候,相似
简略的,咱们能够应用 time.sleep
函数来让主协程阻塞期待
咱们也能够应用 上述提到的 应用 select{} 来达到目标
当然也有其余的形式,后续文章会缓缓的分享到
多个协程
那么多个协程又是怎么玩的呢?
咱们应用 sync.WaitGroup 来实现 goroutine 协程 的同步
package main
import (
"fmt"
"sync"
)
var myWg sync.WaitGroup
func Hi(i int) {
// goroutine 协程 完结就 记录 -1
defer myWg.Done()
fmt.Println("Hello Goroutine! the", i)
}
func main() {
for i := 0; i < 10; i++ {
// 启动一个 goroutine 协程 就记录 +1
myWg.Add(1)
go Hi(i)
}
// 期待所有记录 的 goroutine 协程 都完结
myWg.Wait()}
会有如下输入,每一个协程打印的数字并不是依照程序来的:
Hello Goroutine! the 9
Hello Goroutine! the 4
Hello Goroutine! the 2
Hello Goroutine! the 3
Hello Goroutine! the 6
Hello Goroutine! the 5
Hello Goroutine! the 7
Hello Goroutine! the 8
Hello Goroutine! the 1
Hello Goroutine! the 0
还是同样的,如果是主协程先退出,那么子协程还行持续运行吗?
毋庸置疑,主协程退出,子协程也会跟着退出
GO 中的 协程
分享如下几个点
GO 中的栈是可增长的
个别都有固定的栈内存(通常为 2MB),goroutine 的栈不是固定的,goroutine 的栈大小能够扩大到 1GB
goroutine 是如何调度
这就不得不提 GPM
GPM 是 Go 语言运行时(runtime)层面实现的,咱们先简略理解一下 GPM 别离代表啥
G
就是个 goroutine,外面除了寄存本 goroutine 信息外 还有与所在 P 的绑定等信息
P
Processor 治理着一组 goroutine 队列
P 外面会存储以后 goroutine 运行的上下文环境(函数指针,堆栈地址及地址边界)
P 会对本人治理的 goroutine 队列做一些调度(比方把占用 CPU 工夫较长的 goroutine 暂停、运行后续的 goroutine)
当本人的队列生产完了就去全局队列里取,如果全局队列里也生产完了会去其余 P 的队列里抢工作。
M(machine)
是 Go 运行时(runtime)对操作系统内核线程的虚构
M 与内核线程个别是一一映射的关系,一个 groutine 最终是要放到 M 上执行
这里的 P 与 M 个别也是一一对应的
P 治理着一组 G 挂载在 M 上运行
当一个 G 短暂阻塞在一个 M 上时,runtime 会新建一个 M,
阻塞 G 所在的 P 会把其余的 G 挂载在新建的 M 上
这个时候,当旧的 G 阻塞实现或者认为其曾经挂了的话,就会回收旧的 M
还有一点
P 的个数是通过 runtime.GOMAXPROCS
设定(最大 256),这个数字也依赖于本人的硬件,在并发量大的时候会减少一些 P 和 M,但不会太多
总结
- 分享了并发和并行
- 分享了 GO 的并发,协程的简略应用
- 简略分享了 GO 可伸缩扩大的栈内存
对于 GO 的并发编程知识点涉及面多,对于他的调度原理,真实感趣味的话,能够看上述提到的 GO 并发波及的知识点,一点一点的深刻上来,明天就到这里,大体理解 GO 协程的应用
欢送点赞,关注,珍藏
敌人们,你的反对和激励,是我保持分享,提高质量的能源
好了,本次就到这里,下一次 GO 的锁和原子操作分享
技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。
我是 小魔童哪吒,欢送点赞关注珍藏,下次见~