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稳定之后的事情了,根据历史经验,新特性的引入到稳定被广泛采用至少要一个大版本的跨 ...
随机推荐
- Java基础之(八):顺序结构与选择结构
顺序结构 Java的基本结构就是顺序结构 顺序结构是最简单的算法结构 它是任何一个算法都离不开的一种基本算法结构. 例子: public static void main(String[] args) ...
- Linux——Docker安装
1. 安装Docker i :环境准备:Linux要求内核3.0以上 ii:安装 #1.卸载旧版本 yum remove docker \ docker-client \ docker-client- ...
- 【UE4 C++】调用外部链接库 lib静态库
简述 本例以插件形式测试 使用Lib引用,打包程序运行不用再拷贝lib文件 需要 lib 文件和 .h 头文件 lib部分的代码 .h 头文件 #pragma once #ifndef __MYTES ...
- CentOS系统优化一键配置脚本
#!/usr/bin/env bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export P ...
- Vue 报错Error in render: “TypeError: Cannot read properties of null (reading ‘xxx’)” found in
前端vue报错 [Vue warn]: Error in render: "TypeError: Cannot read properties of null (reading 'name' ...
- 乘风破浪,遇见上一代操作系统Windows 10 - 抢鲜尝试安装新微软商店(Microsoft Store)
背景 在微软官方文章的<十一项关于微软商店新知>中提到: 新的微软商店现在可在Windows 11上找到,我们很高兴地分享,它将在未来几个月内提供给Windows 10客户!我们将很快分享 ...
- CSP-S 2021 爆零记
前言 本人今年高二蒟蒻OIer,高一刚刚接触OI. 感觉可能要直接退役了555~ 希望还有机会靠NOIP翻盘 Day - 暑假 为了备战CSP提前返校,与xzh一起划水,总之刷了不少题,我也大受震撼 ...
- Qt char * 转QString
1.QString转char * 先将QString转换为QByteArray,再将QByteArray转换为char *. 注意:不能用char *mm = str.toLatin1().data( ...
- Luogu P3758 [TJOI2017]可乐 | 矩阵乘法
题目链接 让我们先来思考一个问题,在一张包含$n$个点的图上,如何求走两步后从任意一点$i$到任意一点$j$的方案数. 我们用$F_p(i,j)$来表示走$p$步后从$i$到$j$的方案数,如果存储原 ...
- hadoop前期准备
最近想要学习一下hadoop,现在想边学习边记录下,方便以后自己或别人查看.(注意最好ubantu,jdk及其他软件选择32bit的,jdk最好7以上) 首先配置下jdk,下载下jdk的包,把jdk- ...