引言

说到go语言最厉害的是什么就不得不提到并发,并发是什么?,与并发相关的并行又是什么?

并发:同一时间段内执行多个任务

并行:同一时刻执行多个任务

进程、线程与协程

  • 进程:

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
  • 线程:

    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
  • 协程:

    协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

goroutine

go语言原生支持并发,可以用go关键字快速的让一个函数创建为goroutine协程,也可以创建多个goroutine去执行相同的函数。

sync.WaitGroup可以用来实现goroutine的同步

例如:

  1. var wg sync.WaitGroup
  2. func hello(i int) {
  3. defer wg.Done() // goroutine结束就-1
  4. fmt.Println("Hello Goroutine!", i)
  5. }
  6. func main() {
  7. for i := 0; i < 10; i++ {
  8. wg.Add(1) // 启动一个goroutine就+1
  9. go hello(i)
  10. }
  11. wg.Wait() // 等待所有登记的goroutine都结束
  12. }

最终打印出来的顺序是乱序,因为goroutine是并发操作。

goroutine实际上就是go中的协程,在go语言中可以起成千上万个goroutine协程来进行并发编程

goroutine的调度

goroutine的调度基于GMP模型

  1. G代表一个goroutine对象,每次go调用的时候,都会创建一个G对象
  2. M代表一个线程,每次创建一个M的时候,都会有一个底层线程创建;所有的G任务,最终还是在M上执行
  3. 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是一种特殊的类型,遵循先入先出的规则,保证数据的收发顺序。

无缓冲通道

  1. //创建语法
  2. ch := make(chan int)

无缓冲通道没有容量,因此无缓冲的通道只有在有接收者的时候才能发送,否则会形成死锁,相反如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。

  1. func main() {
  2. ch := make(chan int)
  3. go func() {
  4. fmt.Println(<-ch)
  5. }()
  6. ch <- 10
  7. }

无缓冲管道的本质是为保证同步

有缓冲通道

  1. //创建语法
  2. 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语言并发编程的更多相关文章

  1. Go语言 并发编程

    Go语言 并发编程 作者:Eric 微信:loveoracle11g 1.创建goroutine // 并行 是两个队列同时使用两台咖啡机 // 并发 是两个队列交替使用一台咖啡机 package m ...

  2. 融云开发漫谈:你是否了解Go语言并发编程的第一要义?

    2007年诞生的Go语言,凭借其近C的执行性能和近解析型语言的开发效率,以及近乎完美的编译速度,席卷全球.Go语言相关书籍也如雨后春笋般涌现,前不久,一本名为<Go语言并发之道>的书籍被翻 ...

  3. Go语言并发编程总结

    转自:http://blog.csdn.net/yue7603835/article/details/44309409 Golang :不要通过共享内存来通信,而应该通过通信来共享内存.这句风靡在Go ...

  4. Go语言并发编程示例 分享(含有源代码)

    GO语言并发示例分享: ppt http://files.cnblogs.com/files/yuhan-TB/GO%E8%AF%AD%E8%A8%80.pptx 代码, 实际就是<<Go ...

  5. Python3 与 C# 并发编程之~ 线程篇

      2.线程篇¶ 在线预览:https://github.lesschina.com/python/base/concurrency/3.并发编程-线程篇.html 示例代码:https://gith ...

  6. 11 go并发编程-上

    其他编程语言并发编程的效果 并发编程可以让开发者实现并行的算法以及编写充分利用多核处理器和多核性能的程序.在当前大部分主流的编程语言里,如C,C++,java等,编写维护和调试并发程序相比单线程程序而 ...

  7. golang:并发编程总结

    并行和并发 并发编程是指在一台处理器上"同时"处理多个任务. 宏观并发:在一段时间内,有多个程序在同时运行. 微观并发:在同一时刻只能有一条指令执行,但多个程序指令被快速的轮换执行 ...

  8. 《Go并发编程实战》读书笔记-初识Go语言

    <Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...

  9. c++ 11开始语言本身和标准库支持并发编程

    c++ 11开始语言本身和标准库支持并发编程,意味着真正要到编译器从语言和标准库层面开始稳定,估计得到17标准出来.14稳定之后的事情了,根据历史经验,新特性的引入到稳定被广泛采用至少要一个大版本的跨 ...

随机推荐

  1. Multidimension Tools(多维工具)

    多维工具 # Process: 创建 NetCDF 栅格图层 arcpy.MakeNetCDFRasterLayer_md("", "", "&quo ...

  2. python json中的 dumps loads函数

    一.概念理解 1.json.dumps()和json.loads()是json格式处理函数(可以这么理解,json是字符串) (1)json.dumps()函数是将一个Python数据类型列表进行js ...

  3. 6. 站在巨人的肩膀学习Java Filter型内存马

    本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章: <Tomcat 内存马学习(一):Filter型> <tomcat无文件内存w ...

  4. 敏捷 Scrum Master 的難點

    什麼是 Scrum Master? Scrum master 是一個團隊角色,負責確保團隊遵守敏捷方法和原則並符合團隊的流程和實踐. Scrum Master 促進敏捷開發團隊成員之間的協作.Scru ...

  5. the Agiles Scrum Meeting 11

    会议时间:2020.4.20 20:00 1.每个人的工作 在这次例会上,我们对上周完成的工作进行了总结. 本周已完成的工作 个人结对项目增量开发组 tq: 创建广播功能 修复纯英文数字可能溢出bug ...

  6. nio之缓冲区(Buffer)理解

    一.缓冲区简介 Nio中的 Buffer 是用于存储特定基础类型的一个容器.为了能熟练的使用 Nio中的各种 Buffer , 我们需要理解 Buffer 中的 三个重要 的属性. 1. capaci ...

  7. 2021.1.8 NKOJ 周赛总结

    意料之中..... A:nkoj 3900 AC小程序 http://oi.nks.edu.cn/zh/Problem/Details/3900 A题比较简单,单独分析一下A和C,其实就是一个斐波那契 ...

  8. RAW RGB格式

    RAW RGB格式 10bit Raw RGB, 就是说用10bit去表示一个R, G, 或者B, 通常的都是用8bit的. 所以你后面处理时要把它转换为8bit的, 比较简单的方法就是将低两位去掉, ...

  9. 21.10.14 test

    题目 WOJ5078 到 WOJ5081 T1 Problem A \(\color{green}{100}\) 由于每轮要选择尽量多的边删除,所以想到无向图的生成树,因为在生成树上再加一条边就会形成 ...

  10. MyBatis源码分析(七):动态代理(Mybatis核心机制)

    一.动态代理 动态代理是一种比较高级的代理模式,它的典型应用就是Spring AOP. 在传统的动态代理模式中,客户端通过ProxySubject调用RealSubject类的request( )方法 ...