Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

33|并发:小channel中蕴含大智慧

阻塞特性
判空、判满
计数信号量
消息队列
替代锁机制
信号传递
异步操作
同步操作
make(chan Type, capacity)
make(chan Type)
Channel
Goroutine
其他实用的Channel使用模式分享
nil Channel的妙用
len(channel)的应用
带缓冲Channel
无缓冲Channel
结合time.Ticker实现心跳机制
结合time.After实现超时机制
default分支避免阻塞
同时处理多个Channel操作
关闭后接收操作返回零值和false
使用close函数
带缓冲Channel
无缓冲Channel
使用<-操作符
带缓冲Channel
无缓冲Channel
可以作为函数参数和返回值
使用方式类似普通变量
组成部分
基于CSP理论
思考题
Channel的惯用法
Select语句
关闭Channel
发送与接收
创建Channel
Channel作为一等公民
Go并发模型
Go并发:channel的使用与原理

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
通过上两节课的学习,我们知道了 Go 语言实现了基于 CSP(Communicating Sequential Processes)理论的并发方案。
Go 语言的 CSP 模型的实现包含两个主要组成部分:一个是 Goroutine,它是 Go 应用并发设计的基本构建与执行单元;另一个就是 channel,它在并发模型中扮演着重要的角色。channel 既可以用来实现 Goroutine 间的通信,还可以实现 Goroutine 间的同步。它就好比 Go 并发设计这门“武功”的秘籍口诀,可以说,学会在 Go 并发设计时灵活运用 channel,才能说真正掌握了 Go 并发设计的真谛。
所以,在这一讲中,我们就来系统学习 channel 这一并发原语的基础语法与常见使用方法。

作为一等公民的 channel

Go 对并发的原生支持可不是仅仅停留在口号上的,Go 在语法层面将并发原语 channel 作为一等公民对待。在前面的第 21 讲中我们已经学过“一等公民”这个概念了,如果你记不太清了可以回去复习一下。
那 channel 作为一等公民意味着什么呢?
这意味着我们可以像使用普通变量那样使用 channel,比如,定义 channel 类型变量、给 channel 变量赋值、将 channel 作为参数传递给函数 / 方法、将 channel 作为返回值从函数 / 方法中返回,甚至将 channel 发送到其他 channel 中。这就大大简化了 channel 原语的使用,提升了我们开发者在做并发设计和实现时的体验。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言中的channel是并发编程中的重要组成部分,基于CSP理论,作为一等公民,可以像普通变量一样使用。channel分为无缓冲和带缓冲两种类型,它们在发送和接收操作上有不同的特性。无缓冲channel的发送和接收操作是同步的,需要放在不同的Goroutine中进行,否则会导致死锁。而带缓冲channel的发送和接收操作是异步的,只有在缓冲区满或空时才会阻塞。文章还介绍了channel的性能测试结果,表明带缓冲channel在数据收发的性能上要明显好于无缓冲channel。此外,还介绍了channel的另外两种用法:用作消息队列和用作计数信号量。通过示例展示了如何在实际应用中使用channel进行Goroutine间的通信和同步。文章还介绍了len(channel)的应用,nil channel的妙用以及与select结合使用的一些惯用法。总的来说,本文通过详细的语法介绍和示例说明,帮助读者快速了解了Go并发中channel的基本概念和使用方法,以及其在并发编程中的重要作用。文章还介绍了channel的另外两种用法:利用default分支避免阻塞、实现超时机制和实现心跳机制。这些用法丰富了channel的应用模式,为读者提供了更多实用的并发编程技巧。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(46)

  • 最新
  • 精选
  • peison
    请问计数信号量的例子中,因为jobs的容量是10,这里执行的循环不会导致阻塞,close(jobs) 应该会被执行到,那么下面的for range为什么不会终止,而可以继续运行? go func() { for i := 0; i < 8; i++ { jobs <- (i + 1) } close(jobs) }()

    作者回复: 好问题! channel内部数据是排队的,即便被close,依然可以从closed channel中读取到尚未被消费的元素,直到没有可读的元素为止,才真正会变成closed状态。没数据后,如果再读就会得到元素类型的零值了, 对于没数据且closed状态的channel,for range会终止。

    2022-04-15
    6
    23
  • 张申傲
    这节课信息量有点大,需要多看几遍好好消化。请问老师一个问题:如果程序中没有手动 close channel,那么 channel 会在什么时候关闭呢?是否需要借助 defer 去释放 channel 资源呢?

    作者回复: channel一旦没有人引用了,就会被gc掉,不关闭也ok。但是如果有goroutine一直在读channel,那么channel一直存在,不会关闭。直到程序退出。

    2022-02-20
    13
  • ibin
    白老师,你好,下面这段可以模拟close(groupSignal) for i := 0;i < 5; i++ { groupSignal<-signal(struct{}{}) } 为什么close(groupSignal) 可以给每个groupSignal都发送了{}

    作者回复: close一个channel后,所有阻塞在这个channel接收操作的goroutine都会收到通知,这是Go语言的channel语义就这么定义的。

    2022-01-12
    4
    10
  • 木木
    go的并发原语选择真的是非常精炼:简洁又强大,一个ch就负责了线程通信、同步的多种功能;一个select又实现了对阻塞、非阻塞的控制以及事件循环模式。

    作者回复: 👍

    2022-03-18
    8
  • airmy丶
    请问下老师: 为什么 "1 对 n 的信号通知机制" 这个例子中,wg.Wait() 一定需要新起一个协程执行呢?而且在本地测试确实只能在新的协程中执行才不会报错,否则会报出: goroutine x [chan receive] 这样的错误。

    作者回复: Wait方法的语义就是等待例子中for循环创建的所有子goroutine,直到每个子goroutine都调用完wg.Done才返回。如果不再一个新goroutine执行,wg.Wait就会阻塞住main goroutine,这也将导致后续所有goroutine都阻塞住,然后go运行时检测到所有goroutine都阻塞住了,于是报错退出。

    2022-05-18
    2
    6
  • 瓜牛
    为啥有时需要手动调用close关闭channel,有时又不需要?

    作者回复: 首先明确一点:channel如果不close,也不会存在资源泄露的问题。 是否需要close channel完全看需要。 至于如何知道何时需要,看文中对close channel的语义的描述,以及如何基于这种语义的一些妙用。

    2022-04-20
    2
    5
  • Unknown element
    老师我看makechan的源码发现分配内存的时候分了3种情况: 1. 缓冲区大小=0 2. 元素类型不是指针 3. 元素类型包含指针 我想问下为什么2和3要分成两种情况呢?我看区别好像是调用 mallocgc 时第二个参数不一样,但是mallocgc 的源码我就看不懂了。。。希望老师可以简单解释下 谢谢老师~

    作者回复: 如果channel中的元素大小为0,那就不需要额外分配缓存(buf); 如果元素类型中不含有指针,那么buf就和hchan一起分配(将来也和hchan一起释放),减少一次heap mem分配。GC只扫描hchan就ok了。 如果元素类型包含指针,那么hchan和hchan.buf单独分配内存,GC分别扫描hchan和buf中的元素。

    2022-11-20归属地:北京
    4
  • 每天晒白牙
    感觉只发送channel和只接收channel类型定义符号,交换一下更好理解,也更形象吧 make(chan<- int, 1) 这个代表只接收 make(<-chan int, 1) 这个代表只发送

    作者回复: 由于箭头都是向左的,即<-,所以我区分只发送和只接收型的channel的tip是以 chan这个关键字为中心,将chan关键字看成一个“管子”。当<-在chan的右边,即chan <- 好似向管子里写,这样就是只发送型。当<-在chan的左边,即<- chan ,好似从管子里取,这样就是只接收型。

    2022-07-20
    2
    4
  • 罗杰
    这节课比较绕,要静下心好好学习

    作者回复: 👍

    2022-01-12
    4
  • 怎么睡才能做这种梦
    另外,select 语句中,如果有多个 case 同时都没有阻塞的话,会随机选择一个 case

    作者回复: 👍

    2023-03-06归属地:湖北
    3
收起评论
显示
设置
留言
46
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部