引言

说到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模型

  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是一种特殊的类型,遵循先入先出的规则,保证数据的收发顺序。

无缓冲通道

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

  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. 后缀自动机(SAM)奶妈式教程

    后缀自动机(SAM) 为了方便,我们做出如下约定: "后缀自动机" (Suffix Automaton) 在后文中简称为 SAM . 记 \(|S|\) 为字符串 \(S\) 的长 ...

  2. CORS+XSS的漏洞利用payload

    之前有人问我有没有CORS+XSS的利用姿势,翻了一下国内貌似都没有利用姿势于是就写了这篇文章!!! 首先找到一个反射xss,然后使用xss加载javascript代码达到跨域劫持目的payload如 ...

  3. C语言知识_1

    +,-,*,/是C语言中表示四则运算的符号.:用来分割不同的语句{}用来对语句进行分组 函数代表了一组数据处理过程,由一对大括号所包含的多条语句来表示这个处理过程.每个函数有唯一的名字,main函数是 ...

  4. python常用功能

    1. 获取昨天日期 引入datetime模块 import datetime def getYesterday(): today = datetime.date.today() #返回当前本地日期 # ...

  5. 编程题:X星人的金币

    X星人的金币 时问限制:3000MS 内存限制:589824KB 题目描述: X是人在一艘海底沉船上发现了很多很多很多金币.可爱的X星人决定用这些金币来玩一个填格子的游戏.其规则如下:第1个格子放2枚 ...

  6. 【二食堂】Beta - 设计和计划

    Beta设计和计划 需求再分析 根据助教.老师.用户以及各个团队PM的反馈意见,我们的项目目前有以下问题: 功能不完整 实用价值不高 两方面的缺陷,所以在Beta阶段,我们工作的中心还是完成项目规划中 ...

  7. [对对子队]会议记录5.21(Scrum Meeting8)

    今天已完成的工作 吴昭邦 ​ 工作内容:调整快进按钮 ​ 相关issue:优化流水线加入物品的动画 ​ 相关签入:feat: 快进图标更换,更改第四关材料位置 朱俊豪 ​ 工作内容:调整场景高度和视角 ...

  8. Intellij IDEA 2021.2.3 最新版免费激活教程(可激活至 2099 年,亲测有效)

    ​ 申明,本教程 Intellij IDEA 最新版破解.激活码均收集与网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除.如条件允许,建议大家购买正版. 本教程更新于:2021 年 10 月 ...

  9. stm32电机控制之控制两路直流电机

    小车使用的电机是12v供电的直流电机,带编码器反馈,这样就可以采用闭环速度控制,这里电机使用PWM驱动,速度控制框图如下: 由以上框图可知,STM32通过定时器模块输出PWM波来控制两个直流电机的转动 ...

  10. numpy数组的计算

    1.数组的形状 查看数组的形状: import numpy as np a = np.array([[1, 2, 3, 4, 5], [5, 6, 7, 8, 9]]) print(a.shape) ...