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

31|并发:Go的并发方案实现方案是怎样的?

借鉴CSP模型
goroutine间通信
语言层面支持
Go运行时调度
资源占用小
Tony Bai
分享实用的goroutine使用模式
难以维护
易出错
复杂性高
适应规模化
易于维护
结构清晰
可以同时等待多个通信操作
处理多个channel的发送与接收
可用于同步goroutine
支持数据传输
用于goroutine间通信
同步:需要考虑竞争和锁
通信:通过channel
退出:函数返回即退出
创建:go 关键字
select:多路复用
channel:通信原语
goroutine:轻量级线程
并行的必要条件:多处理器或多核
并发不是并行
并行:执行期概念,多任务同时执行的能力
并发:结构设计相关,多个模块可独立执行
作者
思考题
传统并发模型的问题
并发设计的优势
select
channel
goroutine
Go并发方案
并发与并行
Go并发

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

你好,我是 Tony Bai。
从这一讲开始,我们将会学习这门课的最后一个语法知识:Go 并发。在02 讲中我们提到过:Go 的设计者敏锐地把握了 CPU 向多核方向发展的这一趋势,在决定去创建 Go 语言的时候,他们果断将面向多核、原生支持并发作为了 Go 语言的设计目标之一,并将面向并发作为 Go 的设计哲学。当 Go 语言首次对外发布时,对并发的原生支持成为了 Go 最令开发者着迷的语法特性之一。
那么,怎么去学习 Go 并发呢?我的方法是将“Go 并发”这个词拆开来看,它包含两方面内容,一个是并发的概念,另一个是 Go 针对并发设计给出的自身的实现方案,也就是 goroutine、channel、select 这些 Go 并发的语法特性。
今天这节课,我们就先来了解什么是并发,以及 Go 并发方案中最重要的概念,也就是 goroutine,围绕它基本用法和注意事项,让你对 Go 并发有一个基本的了解,后面我们再层层深入。

什么是并发?

课程一开始,我们就经常提到并发(concurrency)这个词。说了这么长时间的并发,那究竟什么是并发呢?它又与并行(parallelism)有什么区别呢?要想搞清楚这些问题,我们需要简单回顾一下操作系统的基本调度单元的变迁,以及计算机处理器的演化对应用设计的影响。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言通过goroutine、channel和select等语法特性实现了原生支持并发的设计目标。传统编程语言在并发设计上存在诸多不足,如复杂的线程管理、通信困难等问题。相比之下,Go语言的并发方案具有轻量级、通信简单、自动扩展等特点,通过原生支持并发的设计,解决了传统编程语言的诸多问题,为开发者提供了更加便捷、高效的并发编程体验。 Go语言的并发方案主要体现在goroutine和channel的使用上。goroutine作为Go原生支持并发的一个具体实现,具有独立的代码执行流,并与创建它的goroutine一起被Go运行时调度。而channel作为goroutine间的通信原语,为并发设计提供了强大支撑。Go语言借鉴了CSP并发模型,通过channel将goroutine组合连接在一起,让设计和编写大型并发系统变得更加简单和清晰。 总的来说,Go语言通过原生支持并发的设计,解决了传统编程语言在并发方面的诸多问题,为开发者提供了更加便捷、高效的并发编程体验。文章通过介绍并发与并行的区别、传统编程语言在并发设计上的不足之处以及Go语言的并发实现方案,强调了Go语言的改进之处,为读者提供了对Go语言并发方案的全面了解。

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

全部留言(19)

  • 最新
  • 精选
  • pythonbug
    老师好,有个问题想不大清楚:当main里面有go func的时候,是会将go func 放到另外一个处理器执行;还是说当前处理器先执行go func,然后一段时间后回来继续执行main,这样切换来切换去的。

    作者回复: 第一, goroutine是轻量级线程(G),它被调度到执行器M上执行,而不是cpu上。M会被os调度到cpu上执行。 第二,可运行的G先被放入P的队列,每个P绑定一个可以用于执行G的M。之后调度程序可以从P队列中取出G放在M上执行。一个G执行一定时间后,再从队列中取出另一个G运行。 基于上述描述,你的问题,当main中通过go func启动一个新goroutine后,就会有两个可运行的(runnable)的G,新G会被放入P的队列并等待被调度。至于新G是否会与原先的main G分配到一个P上,不确定。如果机器只有一个cpu core。那么显然就如你所说的,新G与main G轮流被调度执行。如果有多个cpu core,那么新G与main G可能就是并行(paralell)执行的。

    2022-04-28
    2
    23
  • lesserror
    大白老师很擅长将复杂的知识深入浅出的讲解出来,这是很多教程没有做到的。读了这一篇,对于Go的并发设计有了新的认识,意犹未尽。另有两处模糊的地方: 1. 文中的 P-n、P-m,这里的n和m应该没有特别的含义吧? 就是指代一个Process而已吧? 2. 文中说:“比如涉及性能敏感的区域或需要保护的结构体数据时”,这里的:结构体数据,应该就是Go的 struct 吧?

    作者回复: 1. 对,n,m只是序号。 2.对。通常我们用struct来抽象事物。保护的也是通常也是这种类型数据。

    2022-01-08
    12
  • 路边的猪
    有个问题请教下各位。 func main() { ctx, cancel := context.WithCancel(context.Background()) go func() { defer func() { fmt.Println("goroutine exit") }() for { select { case <-ctx.Done(): return default: time.Sleep(time.Second) } } }() time.Sleep(time.Second) cancel() time.Sleep(2 * time.Second) } 最近看并发原语这块。一个使用场景是通过cancelContext 来中止一个 执行慢业务的groutine。 有个问题不解,通过select 语句 监听两个 case 。 其中一个case 用于监听 ctx取消的,然后返回终止当前groutine执行。另一个case 用户执行慢业务逻辑。 这里问题是,这个监听的动作需要不停的去for循环 检查ctx.Done ,但是真正的慢业务 会阻塞 select啊。也就检查不到 ctx.Done啊,还怎么起到 通过ctx 控制取消慢业务groutine的作用呢? 比如这里的default语句 如果里面的业务不是睡眠1秒而是发起了一个网络调用需要很久,那即使 下面cancel() 被调用 select语句中依然会被阻塞在 网络调用里。

    作者回复: 好问题! 我的理解是:那就需要那个慢业务调用本身也支持Context。很多人说context.Context有“传染”效应,大概就是这个意思。

    2022-06-16
    4
    6
  • Junior Programmer
    也想请问一下老师,能不能顺便讲解一下,同步和异步的概念

    作者回复: 同步与异步的概念应用很广,在很多领域都有应用。比如通信领域,比如并发编程领域。 这一讲的同步设计中的“同步(sync)”指的是并发编程中对临界区进行“互斥”访问的概念,即有且仅有一个goroutine可以进入临界区,操作临界区的数据。其他goroutine只能在临界区外“排队”等待时机进入。 不过无论用在那个领域,“同步”与“异步”的通用含义是: 同步操作:执行流 只有等待发起的同步操作完成后,才能继续向下执行。 异步操作:发起异步操作后,原执行流可以无需等待,即可向下继续执行。

    2022-06-12
    6
  • 罗杰
    作为一个开发者,还是要尽早了解并发和并行的区别,这节课要好好的学习和理解。

    作者回复: 👍

    2022-01-08
    6
  • 左耳朵东
    可不可以这样理解:输入输出原语应用在函数上就是函数签名(参数、返回值),应用到 goroutine 之间就是 channel。具体一点,在函数场景想要输入马上想到通过参数传入,想要输出通过返回值给;在 goroutine 之间,想要输入则马上想到通过 channel 拿,想要把处理结果输出,放到 channel 中就行了。

    作者回复: 有点这个意思。

    2022-11-04归属地:北京
    5
  • Junior Programmer
    读了这节课,让我清晰认识到之前的的一个误区,就是并发和并行的区别。并发:针对的是程序结构设计,将一个程序分成若干个模块,不同模块单独执行,由多个模块相互交替执行,实现程序的运行。并行:针对的程序的执行,指的是同一时间点,有个多个任务在被多个或者多核的CPU下调度执行。

    作者回复: 👍

    2022-06-12
    3
    4
  • 阿星
    func spawn(f func() error) <-chan error { c := make(chan error) go func() { c <- f() }() return c } func main() { c := spawn(func() error { time.Sleep(2 * time.Second) return errors.New("timeout") }) fmt.Println("hello,world1") fmt.Println(<-c) fmt.Println("hello,world2") } 通过对上面的代码测试后我的理解是这样的: spawn中的 go func() 是异步执行(可能与主线程是并行), 所以在主线程中的spawn不会阻塞,先打印出了 "hello,world1", 等到打印 <-c 的时候就阻塞了,等从c 中读出数据打印结束后才会继续执行后续的打印 "hello,world2"。 所以说从channel 中读取数据可以认为肯定是一个阻塞的同步数据操作,不知道我的理解对吗?

    作者回复: ✅👍。

    2023-01-23归属地:四川
    3
  • aoe
    CSP、响应式编程简介: CSP(Communicationing Sequential Processes,通信顺序进程)并发模型 响应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和变化传播的声明式编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。 CSP、响应式编程两者的思想非常像(以我目前的理解没看出区别),但是使用Java、Scala之类的语言实现响应式编程一般需要借助额外的框架编程(例如 Reactor)。 但使用 Go 编程居然是语言自带的特性,一个关键字 go 就行了!

    作者回复: 👍

    2022-01-11
    3
  • 菠萝吹雪—Code
    这节讲的太好了,并发和并行理解透了

    作者回复: 👍

    2022-09-06归属地:北京
    2
收起评论
显示
设置
留言
19
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部