GO语言学习笔记-并发篇 Study for Go ! Chapter seven - Concurrency
持续更新 Go 语言学习进度中 ......
- GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-表达式篇 Study for Go ! Chapter two - Expression - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-函数篇 Study for Go ! Chapter three - Function - slowlydance2me - 博客园 (cnblogs.com)
Study for Go ! Chapter seven - Concurrency
1. What is concurrency ?
concurrency vs parallelism
并发:逻辑上具备同时处理多个任务的能力
并行:物理上在同一时刻执行多个并发任务
通常说的程序是并发设计的,也就是说它允许多个任务同时执行,但实际上并不一定真的在同一时刻发生
单核处理器以间隔方式执行
并行则依赖多核处理器等物理设备,让多个任务真正在同一时刻执行
并行是并发设计的理想执行模式
多线程或多进程是并行的基本条件,但是单线程也可用协程(coroutine)做到并发,尽管协程在单个线程上通过主动切换来实现多任务并发,但它也有自己的优势。除了将因阻塞而浪费的时间找回来以外,还免去了线程切换开销,有着不错的执行效率。协程上运行的多个任务本质上是依旧串行的,加上可控自主调度,所以并不需要做同步处理
即便采用多线程也未必就能并行,Python 就因 GIL 限制,默认只能并发而不能并行,所以很多时候转用 " 多进程 + 协程 " 架构
各种方式都有各自的使用场景,通常情况下,用多进程来实现分布式和负载平衡,减轻单进程垃圾回收能力;用多线程 (LWP)抢夺更多的处理器资源;用协程来提高处理器时间片利用率
而简单将 goroutine 归纳为协程并不合适,运行时会创建多个线程来执行并发任务,且任务单元可被调度到其他线程并行执行,这更像是多线程和协程的综合体,能最大限度提升执行效率,发挥多核处理能力
只需在函数调用前添加 go 关键字即可创建并发任务
关键字 go 并非执行并发操作,而是创建一个并发任务单元。新建任务被放置在系统队列中,等待调度器安排合适系统线程去获取执行权。当前流程不会阻塞,不会等待该任务启动,且运行时也不保证并发任务的执行次序
每个任务单元除保存函数指针,调用参数外,还会分配执行所需的栈内存空间
相比 默认 MB 级别的线程栈, goroutine 自定义栈初试仅需 2 KB,所以才能创建成千上万的并发任务
自定义栈采取按需分配策略,在需要时进行扩容,最大能到 GB 规模
与 defer 一样, goroutine 也会因 “ 延迟执行 ” 而立即计算并复制执行参数
Wait
进程推出时不会等待并发任务结束,可用通道 (channel)阻塞,然后发出退出信号
除关闭通道外,写入数据也可解除阻塞。
如想要等待多个任务结束,推荐使用 sync.WaitGroup。通过设定计数器,让每个 goroutine 在退出前递减,直至归零时解除阻塞
尽管 WaitGroup.Add 实现了原子操作,但建议在goroutine 外累加计数器,以免 Add 尚未执行,Wait 已经退出
可在多处使用 Wait 阻塞,它们都能接收到通知
GOMAXPROCS
运行时可能会创建很多线程,但任何时候仅有限的几个线程参与并发任务执行,该数量默认与处理器核数相等,可用 runtime.GOMAXPROCS 函数 (或环境变量)修改
(如参数小于1,GOMAXPROCS 仅返回当前设置值,不做任何调整)
Local Storage
与线程不同,goroutine 任务无法设置优先级,无法获取编号,没有局部存储(TLS),甚至连返回值都会被抛弃。但除优先级外,其他功能都很容易实现
使用 map 作为局部存储容器,当前任务被放回队列,等待下次调度时恢复执行
Gosched
暂停,释放线程去执行其他任务。当前任务被放回队列,等待下次调度时恢复执行
该函数很少被使用,因为运行时会主动向长时间(10 ms)的任务发出抢占调度,只是当前版本实现的算法稍显粗糙,不能保证调度总能成功,所以主动切换还有使用场合
Goexit
Goexit 立即终止当前任务,运行时确保所有已注册延迟调用被执行。该函数不会影响其他并发任务,不会引发 panic,自然也无法捕获
如果在main.main 里调用 Goexit,它会等待其他任务结束,然后让进程直接崩溃
无论身处那一层,Goexit 都能立即终止整个调用堆栈,这与 return 仅退出当前函数不同,标准库函数 os.Exit 可终止进程,但不会执行延迟调用
2. Channel
相比 Erlang,golang 并未实现严格的并发安全
允许全局变量、指针、引用类型这些非安全内存共享操作,就需要开发人员自行维护数据一致性和完整性。golang 鼓励使用 CSP 通道,以通信来代替内存安全共享,实现并发安全
通过消息来避免竞态的模型除了 CSP,还有Actor。它们有较大区别
作为 CSP 核心,通道 (channel)是显式的,要求操作双方必须知道数据类型和具体通道,并不关系另一端操作者身份和数量。可如果另一端未准备妥当,或消息未能即使处理时,会阻塞当前端
而 Actor 是透明的,它不在乎数据类型及通道,只要知道接收者信箱即可,默认是异步方式,发送方对消息是否被接受和处理并不关心。
从层次实现上来说,通道只是一个队列,同步模式下,发送和接受双方配对,然后直接复制数据给对方,如果配对失败,则置入等待队列,直到另一方出现后才被唤醒。异步模式抢夺的则是数据缓冲槽。发送方要求有空槽可供写入,而接收方则要求有缓冲数据可读。需求不符时,同样加入等待队列,直到有另一方写入数据或腾出空槽后被唤醒
除传递消息(数据)外,通道还常被用作事件通知
同步模式必须有配对操作的 goroutine 出现,否则会一直阻塞。而异步模式在缓冲区未满或数据未读完前,不会阻塞
多数时候,异步通道有助于提升性能,减少排队阻塞
缓冲区大小只是内部属性,不属于类型组成成分,另外通道变量本身就是指针,可用相等操作符判断是否为同一对象或 nil
内置函数 cap 和 len 返回缓冲区大小和当前已缓冲数量;而对于同步通道则都返回 0,据此可判断通道是同步还是异步
收发
除了使用简单的发送和接受操作符以外,还可以用 ok-idom 或 range 模式处理数据
对于循环接受数据,range 模式更简洁一点
及时使用 close 函数关闭通道引发结束通知,否则可能会导致死锁
通知可以是群体性的,也未必就是结束,可以是任何需要表达的事件

对于 closed 或 nil 通道,发送和接受都有相规则:
向已关闭通道发送数据,引发 panic
从已关闭通道接收数据,返回已缓冲数据或 0 值
无论收发,nil 通道都会阻塞
重复关闭,或关闭 nil 通道 都会引发 panic
单向
通道默认是双向的,并不区分发送和接收端。但某些时候,我们可限制收发操作的方向,来获得更严谨的操作逻辑
尽管可用 make 创建 单向通道,但那没有任何意义。通常使用类型转换来获取单向通道,并分别赋予操作双方
不能再单向通道上做逆向操作
同样, close 不能用于接收端
也无法将单向通道重新转换回去
选择
如要同时处理多个通道,可选用 select 语句,它会随机选择一个可用通道做收发操作
如要等全部通道消息处理结束 ( closed ), 可将已完成通道设置为nil。这样它就会被阻塞,不再被 select 选中
及时是同一通道,也会随机选择case执行
当所有通道都不可用时,select 会执行 default 语句,如此可避开 select 阻塞,但须注意处理外层循环,以免陷入空耗
也可用 default 处理一些默认逻辑
模式
通常使用工厂方法将 goroutine 和通道绑定
介于通道本身就是一个并发安全的队列,可用作 ID generator、Pool 等用途
可用通道实现 信号量 (samaphore)
性能
将发往通道的数据打包,减少传输次数,可有效提升性能。从实现上来说,通道队列依旧使用锁同步机制,单次获取更多数据 (批处理),可改善因频繁加锁造成的性能问题
资源泄漏
通道可能会引发 goroutine leak, 确切地说,是指 goroutine 处于发送或接受阻塞状态,但一直未被唤醒,垃圾回收器并不收集此类资源,导致它们会在等待队列里长久休眠,导致资源泄漏
3. 同步
通道并非用来代替锁的,它们各自有不同的使用场景。通道倾向于解决逻辑层次的并发处理架构,而锁用来保护局部范围内的数据安全
标准库 sync 提供了互斥和读写锁,另有原子操作等,可基本满足日常开发需要,Mutex,RWMutex 的使用并不复杂,只有几个地方需要注意
将 Mutex 作为匿名字段时,相关方法必须实现为 pointer-receiver,否则会因复制导致锁机制失效
应将 Mutex 锁粒度控制在最小范围内,及早释放
Mutex 不支持递归锁,即便在同一 goroutine 下也会导致死锁
GO语言学习笔记-并发篇 Study for Go ! Chapter seven - Concurrency的更多相关文章
- C语言学习笔记——特别篇(VScode安装使用)
B站有同步教学视频 参考博文: https://www.cnblogs.com/czlhxm/p/11794743.html 注意事项: 请在英文目录下运行!!! VScode下载链接: https: ...
- 大一C语言学习笔记(5)---函数篇-定义函数需要了解注意的地方;定义函数的易错点;详细说明函数的每个组合部分的功能及注意事项
博主学习C语言是通过B站上的<郝斌C语言自学教程>,对于C语言初学者来说,我认为郝斌真的是在全网C语言学习课程中讲的最全面,到位的一个,这个不是真不是博主我吹他哈,大家可以去B站去看看,C ...
- PHP学习笔记 - 进阶篇(7)
PHP学习笔记 - 进阶篇(7) 文件操作 读取文件内容 PHP具有丰富的文件操作函数,最简单的读取文件的函数为file_get_contents,可以将整个文件全部读取到一个字符串中. $conte ...
- PHP学习笔记 - 进阶篇(6)
PHP学习笔记- 进阶篇(6) 会话控制(session与cookie) 当前的Cookie为: cookie简介 Cookie是存储在客户端浏览器中的数据,我们通过Cookie来跟踪与存储用户数据. ...
- PHP学习笔记 - 进阶篇(2)
PHP学习笔记 - 进阶篇(2) 函数 1.自定义函数 PHP内置了超过1000个函数,因此函数使得PHP成为一门非常强大的语言.大多数时候我们使用系统的内置函数就可以满足需求,但是自定义函数通过将一 ...
- PHP学习笔记 - 入门篇(5)
PHP学习笔记 - 入门篇(5) 语言结构语句 顺序结构 eg: <?php $shoesPrice = 49; //鞋子单价 $shoesNum = 1; //鞋子数量 $shoesMoney ...
- PHP学习笔记--入门篇
PHP学习笔记--入门篇 一.Echo语句 1.格式 echo是PHP中的输出语句,可以把字符串输出(字符串用双引号括起来) 如下代码 <?php echo "Hello world! ...
- 数据库MySQL学习笔记高级篇
数据库MySQL学习笔记高级篇 写在前面 学习链接:数据库 MySQL 视频教程全集 1. mysql的架构介绍 mysql简介 概述 高级Mysql 完整的mysql优化需要很深的功底,大公司甚至有 ...
- HTML语言学习笔记(会更新)
# HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...
- PHP学习笔记 - 进阶篇(11)
PHP学习笔记 - 进阶篇(11) 数据库操作 PHP支持哪些数据库 PHP通过安装相应的扩展来实现数据库操作,现代应用程序的设计离不开数据库的应用,当前主流的数据库有MsSQL,MySQL,Syba ...
随机推荐
- JQuery 页面滚动至指定元素位置
$(window).scrollTop($("#id").offset().top - 20);
- 2022.3.9内部群每日三题-清辉PMP
1.项目经理集合在地理上分散的团队,为一家组织实施新的强制性监管要求.若要获得该相关方的承诺,项目经理应该怎么做? A.设置必要的沟通基础设施 B.召开项目启动大会 C.执行相关方分析 D.让团队集中 ...
- 实现MybatisPlus乐观锁
1.实体类中添加version字段及相关注解 @Version@TableField(fill = FieldFill.INSERT)//第一次添加数据时使其有个默认值1private Integer ...
- lui - pager - 分页
pager - 分页 demo lui demo <template> <div class="app-container"> <h3>el-p ...
- IDEA+SpringBoot整合Swagger2创建API文档
------------恢复内容开始------------ 1.创建SpringBoot项目 2.选择快捷方式创建springboot项目 3.工程文件树形图 4.pom.xml中导入Swagger ...
- Git配置新学
Git中的AutoCRLF与SafeCRLF换行符问题 https://zhuanlan.zhihu.com/p/380574688 https://xiaozhuanlan.com/topic/40 ...
- Gstreamer 随笔
1. Gstreamer在Ubuntu上需要安装得全部库: gstreamer1.0-alsa - GStreamer plugin for ALSAgstreamer1.0-clutter-3.0 ...
- Winform多线程访问UI控件问题
Winform多线程无法访问UI控件,考虑使用委托方法解决.
- Linux系统管理实战-配置静态IP
配置静态IP 前置条件 防火墙: EL7 EL6 查看状态: # systemctl status firewalld # /etc/init.d/iptables status 立即关闭: # sy ...
- org.nutz.http.Http忽略https SSL证书验证
访问的是一个https get请求,报错需要SSL证书验证,以下方法直接跳过 boolean check = Http.disableJvmHttpsCheck(); // 忽略https的证书检查