持续更新 Go 语言学习进度中 ......

  1. GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs.com)
  2. GO语言学习笔记-表达式篇 Study for Go ! Chapter two - Expression - slowlydance2me - 博客园 (cnblogs.com)
  3. GO语言学习笔记-函数篇 Study for Go ! Chapter three - Function - slowlydance2me - 博客园 (cnblogs.com)

Study for Go ! Chapter seven - Concurrency

1. What is concurrency ?

  1. 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 函数关闭通道引发结束通知,否则可能会导致死锁

  • 通知可以是群体性的,也未必就是结束,可以是任何需要表达的事件

    ![image-20230307164012213](C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20230307164012213.png)

  • 对于 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的更多相关文章

  1. C语言学习笔记——特别篇(VScode安装使用)

    B站有同步教学视频 参考博文: https://www.cnblogs.com/czlhxm/p/11794743.html 注意事项: 请在英文目录下运行!!! VScode下载链接: https: ...

  2. 大一C语言学习笔记(5)---函数篇-定义函数需要了解注意的地方;定义函数的易错点;详细说明函数的每个组合部分的功能及注意事项

    博主学习C语言是通过B站上的<郝斌C语言自学教程>,对于C语言初学者来说,我认为郝斌真的是在全网C语言学习课程中讲的最全面,到位的一个,这个不是真不是博主我吹他哈,大家可以去B站去看看,C ...

  3. PHP学习笔记 - 进阶篇(7)

    PHP学习笔记 - 进阶篇(7) 文件操作 读取文件内容 PHP具有丰富的文件操作函数,最简单的读取文件的函数为file_get_contents,可以将整个文件全部读取到一个字符串中. $conte ...

  4. PHP学习笔记 - 进阶篇(6)

    PHP学习笔记- 进阶篇(6) 会话控制(session与cookie) 当前的Cookie为: cookie简介 Cookie是存储在客户端浏览器中的数据,我们通过Cookie来跟踪与存储用户数据. ...

  5. PHP学习笔记 - 进阶篇(2)

    PHP学习笔记 - 进阶篇(2) 函数 1.自定义函数 PHP内置了超过1000个函数,因此函数使得PHP成为一门非常强大的语言.大多数时候我们使用系统的内置函数就可以满足需求,但是自定义函数通过将一 ...

  6. PHP学习笔记 - 入门篇(5)

    PHP学习笔记 - 入门篇(5) 语言结构语句 顺序结构 eg: <?php $shoesPrice = 49; //鞋子单价 $shoesNum = 1; //鞋子数量 $shoesMoney ...

  7. PHP学习笔记--入门篇

    PHP学习笔记--入门篇 一.Echo语句 1.格式 echo是PHP中的输出语句,可以把字符串输出(字符串用双引号括起来) 如下代码 <?php echo "Hello world! ...

  8. 数据库MySQL学习笔记高级篇

    数据库MySQL学习笔记高级篇 写在前面 学习链接:数据库 MySQL 视频教程全集 1. mysql的架构介绍 mysql简介 概述 高级Mysql 完整的mysql优化需要很深的功底,大公司甚至有 ...

  9. HTML语言学习笔记(会更新)

    # HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...

  10. PHP学习笔记 - 进阶篇(11)

    PHP学习笔记 - 进阶篇(11) 数据库操作 PHP支持哪些数据库 PHP通过安装相应的扩展来实现数据库操作,现代应用程序的设计离不开数据库的应用,当前主流的数据库有MsSQL,MySQL,Syba ...

随机推荐

  1. JQuery 页面滚动至指定元素位置

    $(window).scrollTop($("#id").offset().top - 20);

  2. 2022.3.9内部群每日三题-清辉PMP

    1.项目经理集合在地理上分散的团队,为一家组织实施新的强制性监管要求.若要获得该相关方的承诺,项目经理应该怎么做? A.设置必要的沟通基础设施 B.召开项目启动大会 C.执行相关方分析 D.让团队集中 ...

  3. 实现MybatisPlus乐观锁

    1.实体类中添加version字段及相关注解 @Version@TableField(fill = FieldFill.INSERT)//第一次添加数据时使其有个默认值1private Integer ...

  4. lui - pager - 分页

    pager - 分页 demo lui demo <template> <div class="app-container"> <h3>el-p ...

  5. IDEA+SpringBoot整合Swagger2创建API文档

    ------------恢复内容开始------------ 1.创建SpringBoot项目 2.选择快捷方式创建springboot项目 3.工程文件树形图 4.pom.xml中导入Swagger ...

  6. Git配置新学

    Git中的AutoCRLF与SafeCRLF换行符问题 https://zhuanlan.zhihu.com/p/380574688 https://xiaozhuanlan.com/topic/40 ...

  7. Gstreamer 随笔

    1. Gstreamer在Ubuntu上需要安装得全部库: gstreamer1.0-alsa - GStreamer plugin for ALSAgstreamer1.0-clutter-3.0  ...

  8. Winform多线程访问UI控件问题

    Winform多线程无法访问UI控件,考虑使用委托方法解决.

  9. Linux系统管理实战-配置静态IP

    配置静态IP 前置条件 防火墙: EL7 EL6 查看状态: # systemctl status firewalld # /etc/init.d/iptables status 立即关闭: # sy ...

  10. org.nutz.http.Http忽略https SSL证书验证

    访问的是一个https get请求,报错需要SSL证书验证,以下方法直接跳过 boolean check = Http.disableJvmHttpsCheck(); // 忽略https的证书检查