go语言并发编程
引言
说到go语言最厉害的是什么就不得不提到并发,并发是什么?,与并发相关的并行又是什么?
并发:同一时间段内执行多个任务
并行:同一时刻执行多个任务

进程、线程与协程
- 进程:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。 - 线程:
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。 - 协程:
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
goroutine
go语言原生支持并发,可以用go关键字快速的让一个函数创建为goroutine协程,也可以创建多个goroutine去执行相同的函数。
sync.WaitGroup可以用来实现goroutine的同步
例如:
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // goroutine结束就-1
fmt.Println("Hello Goroutine!", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) // 启动一个goroutine就+1
go hello(i)
}
wg.Wait() // 等待所有登记的goroutine都结束
}
最终打印出来的顺序是乱序,因为goroutine是并发操作。
goroutine实际上就是go中的协程,在go语言中可以起成千上万个goroutine协程来进行并发编程
goroutine的调度
goroutine的调度基于GMP模型
- G代表一个goroutine对象,每次go调用的时候,都会创建一个G对象
- M代表一个线程,每次创建一个M的时候,都会有一个底层线程创建;所有的G任务,最终还是在M上执行
- P代表一个处理器,每一个运行的M都必须绑定一个P,就像线程必须在么一个CPU核上执行一样

P的个数就是GOMAXPROCS(最大256),启动时固定的,一般不修改; M的个数和P的个数不一定一样多(会有休眠的M或者不需要太多的M)(最大10000);每一个P保存着本地G任务队列,也有一个全局G任务队列;
并发安全
go原生提供并发原语goroutine和channel为构造并发提供了一种优雅而简单的方式,go没有显示的利用锁来控制并发安全,而是鼓励提倡通过通信共享内存而不是通过共享内存而实现通信。
sync.atomic
Go语言中原子操作由内置的标准库sync/atomic提供。
这些功能需要非常小心才能正确使用。 除特殊的底层应用程序外,同步更适合使用channel或sync包的功能。 通过消息共享内存; 不要通过共享内存进行通信。

Mutex
互斥锁是一种常用的共享资源访问的方法,它能够保证同时只有一个goroutine可以访问资源。Go语言中使用sync包的Mutex类型来实现互斥锁。

go在1.8默认使用自旋模式,当试图获取已经被持有的锁时,如果本地队列为空并且 P 的数量大于1,goroutine 将自旋几次(用一个 P 旋转会阻塞程序)。自旋后,goroutine park。在程序高频使用锁的情况下,它充当了一个快速路径。
go在1.9新增了Starving模式,当自旋模式抢到锁,表示有协程释放了锁,如果waiter>0,即有阻塞等待的协程,会释放信号量来唤醒协程,当协程被唤醒后,发现Locked=1,锁又被抢占,则又会阻塞,但在阻塞前会判断自上次阻塞到本次阻塞经历了多长时间,如果超过1ms的话,会将Mutex标记为"饥饿"模式,然后再阻塞。当被标记为饥饿状态时,unlock 方法会 handsoff 把锁直接扔给第一个等待者。
在饥饿模式下,自旋也被停用,因为传入的goroutines 将没有机会获取为下一个等待者保留的锁。

RWMutex
互斥锁是完全互斥的,但是有很多场景下读多写少,因此我们并发去读取一个资源而不涉及到资源修改的时候是完全没必要加锁的,这种情况下读写锁是一种更好的选择。
读写锁分为读锁和写锁,读锁与读锁兼容,读锁与写锁互斥,写锁与写锁互斥。
errgroup
ErrGroup是 Go 官方提供的一个同步扩展库。可以将一个大任务拆分成几个小任务并发执行,提高程序效率。sync.ErrGroup在sync.WaitGroup功能的基础上,增加了错误传递,以及在发生不可恢复的错误时取消整个goroutine集合,或者等待超时
sync.pool
go语言为了降低GC压力引入了sync.Pool对象池用来保存和复用临时对象。sync.Pool是可伸缩的,并发安全的。其大小仅受限于内存的大小。sync.pool对象池比较适合用来存储一些临时切状态无关的数据,但是不适合用来做连接池,因为存入对象池中的值有可能会在垃圾回收时被删除掉
在go的1.13版本中引入了victim cache,会将pool内数据拷贝一份,避免GC将其清空,即使没有引用的内容也可以保留最多两轮GC.
channel
channel是一种类型安全的消息队列,用以充当两个goroutine之间的消息通道。go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
go语言中的channel是一种特殊的类型,遵循先入先出的规则,保证数据的收发顺序。
无缓冲通道
//创建语法
ch := make(chan int)
无缓冲通道没有容量,因此无缓冲的通道只有在有接收者的时候才能发送,否则会形成死锁,相反如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
func main() {
ch := make(chan int)
go func() {
fmt.Println(<-ch)
}()
ch <- 10
}
无缓冲管道的本质是为保证同步
有缓冲通道
//创建语法
ch := make(chan int, 10) //创建缓冲为10的通道
只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量,当通道的容量已满时将会阻塞发送者使其等待缓冲通道可用,而当缓冲通道为空的时候会阻塞接收者使其等待资源被发送。
channel内置的len函数可以获取通道内元素的数量,使用cap函数获取通道的容量。
常见异常

References
https://www.cnblogs.com/lxmhhy/p/6041001.html
https://blog.csdn.net/liangzhiyang/article/details/52669851
https://www.cnblogs.com/sunsky303/p/9705727.html
https://zhuanlan.zhihu.com/p/265670936
https://zhuanlan.zhihu.com/p/88878287
https://www.bilibili.com/read/cv10112308/
https://pkg.go.dev/golang.org/x/sync/errgroup
https://mp.weixin.qq.com/s/NcrENqRyK9dYrOBBI0SGkA
https://www.jianshu.com/p/8fbbf6c012b2
https://www.jianshu.com/p/24ede9e90490
https://www.liwenzhou.com/posts/Go/14_concurrence/#autoid-1-4-3
go语言并发编程的更多相关文章
- Go语言 并发编程
Go语言 并发编程 作者:Eric 微信:loveoracle11g 1.创建goroutine // 并行 是两个队列同时使用两台咖啡机 // 并发 是两个队列交替使用一台咖啡机 package m ...
- 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
2007年诞生的Go语言,凭借其近C的执行性能和近解析型语言的开发效率,以及近乎完美的编译速度,席卷全球.Go语言相关书籍也如雨后春笋般涌现,前不久,一本名为<Go语言并发之道>的书籍被翻 ...
- Go语言并发编程总结
转自:http://blog.csdn.net/yue7603835/article/details/44309409 Golang :不要通过共享内存来通信,而应该通过通信来共享内存.这句风靡在Go ...
- Go语言并发编程示例 分享(含有源代码)
GO语言并发示例分享: ppt http://files.cnblogs.com/files/yuhan-TB/GO%E8%AF%AD%E8%A8%80.pptx 代码, 实际就是<<Go ...
- Python3 与 C# 并发编程之~ 线程篇
2.线程篇¶ 在线预览:https://github.lesschina.com/python/base/concurrency/3.并发编程-线程篇.html 示例代码:https://gith ...
- 11 go并发编程-上
其他编程语言并发编程的效果 并发编程可以让开发者实现并行的算法以及编写充分利用多核处理器和多核性能的程序.在当前大部分主流的编程语言里,如C,C++,java等,编写维护和调试并发程序相比单线程程序而 ...
- golang:并发编程总结
并行和并发 并发编程是指在一台处理器上"同时"处理多个任务. 宏观并发:在一段时间内,有多个程序在同时运行. 微观并发:在同一时刻只能有一条指令执行,但多个程序指令被快速的轮换执行 ...
- 《Go并发编程实战》读书笔记-初识Go语言
<Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...
- c++ 11开始语言本身和标准库支持并发编程
c++ 11开始语言本身和标准库支持并发编程,意味着真正要到编译器从语言和标准库层面开始稳定,估计得到17标准出来.14稳定之后的事情了,根据历史经验,新特性的引入到稳定被广泛采用至少要一个大版本的跨 ...
随机推荐
- scratch塔罗牌的制作
首先,这个程序的流程是洗牌->占卜,很简单的一个程序.那个程序的组成是什么呢? 该程序由22张大卡.开始洗牌按钮.占卜按钮和说出占卜结果的角色组成. 先来说说开始洗牌按钮吧. 开始的时候移动到相 ...
- 第6次 Beta Scrum Meeting
本次会议为Beta阶段第6次Scrum Meeting会议 会议概要 会议时间:2021年6月8日 会议地点:「腾讯会议」线上进行 会议时长:15min 会议内容简介:对完成工作进行阶段性汇报:对下一 ...
- 单片机I/O口推挽与开漏输出详解(力荐)
推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止. 开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电 ...
- 算法:杨辉三角(Pascal's Triangle)
一.杨辉三角介绍 杨辉三角形,又称帕斯卡三角形.贾宪三角形.海亚姆三角形.巴斯卡三角形,是二项式系数的一种写法,形似三角形,在中国首现于南宋杨辉的<详解九章算法>得名,书中杨辉说明是引自贾 ...
- 手写vue-router & 什么是Vue插件
博文分享 这篇文章你可以学习到: 实现一个自己的vue-router 了解什么是Vue的插件 学习b站大佬后做的笔记整理和源码实现 1.1.3一步一步带你弄懂vue-router核心原理及实现哔哩哔哩 ...
- cURL 命令获取本机外网 IP
1.1 查询本机外网 IP # curl dhcp.cn 134.175.159.160 1.2 输出格式为 JSON # curl dhcp.cn/?json { "IP": & ...
- (二)lamp环境搭建之编译安装mysql
mysql 编译安装1,在网站上下载: wget http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.15.tar.gz 2,安装cmake ...
- Linux题目学习
一.填空题: 1. 在Linux系统中,以 文件 方式访问设备 . 2. Linux内核引导时,从文件/etc/fstab 中读取要加载的文件系统. 3. Linux文件系统中每个文件用 i节点来标识 ...
- 攻防世界 Misc 新手练习区 stegano CONFidence-DS-CTF-Teaser Writeup
攻防世界 Misc 新手练习区 stegano CONFidence-DS-CTF-Teaser Writeup 题目介绍 题目考点 隐写术 摩斯密码 Writeup 下载附件是PDF文件打开,研究一 ...
- rz安装
rpm -ivh http://www.rpmfind.net/linux/centos/6.10/os/x86_64/Packages/lrzsz-0.12.20-27.1.el6.x86_64.r ...