瞅一眼就能学会的GO并发编程使用教程
来源:脚本之家
时间:2023-02-25 08:05:05 113浏览 收藏
大家好,今天本人给大家带来文章《瞅一眼就能学会的GO并发编程使用教程》,文中内容主要涉及到go并发、编程,如果你对Golang方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!
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
会有如下输出,每一个协程打印的数字并不是按照顺序来的:
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并发编程使用教程》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!
-
332 收藏
-
429 收藏
-
105 收藏
-
360 收藏
-
102 收藏
-
290 收藏
-
239 收藏
-
381 收藏
-
168 收藏
-
500 收藏
-
355 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习