Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24139 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

32 | context.Context类型

郝林 2018-10-24
我们在上篇文章中讲到了sync.WaitGroup类型:一个可以帮我们实现一对多 goroutine 协作流程的同步工具。
在使用WaitGroup值的时候,我们最好用“先统一Add,再并发Done,最后Wait”的标准模式来构建协作流程。
如果在调用该值的Wait方法的同时,为了增大其计数器的值,而并发地调用该值的Add方法,那么就很可能会引发 panic。
这就带来了一个问题,如果我们不能在一开始就确定执行子任务的 goroutine 的数量,那么使用WaitGroup值来协调它们和分发子任务的 goroutine,就是有一定风险的。一个解决方案是:分批地启用执行子任务的 goroutine。

前导内容:WaitGroup 值补充知识

我们都知道,WaitGroup值是可以被复用的,但需要保证其计数周期的完整性。尤其是涉及对其Wait方法调用的时候,它的下一个计数周期必须要等到,与当前计数周期对应的那个Wait方法调用完成之后,才能够开始。
我在前面提到的可能会引发 panic 的情况,就是由于没有遵循这条规则而导致的。
只要我们在严格遵循上述规则的前提下,分批地启用执行子任务的 goroutine,就肯定不会有问题。具体的实现方式有不少,其中最简单的方式就是使用for循环来作为辅助。这里的代码如下:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(26)

  • Cloud
    还没用过context包的我看得一愣一愣的
    2018-10-24
    56
  • Spike
    https://blog.golang.org/pipelines
    https://blog.golang.org/context
    要了解context的来源和用法,建议先阅读官网的这两篇blog
    2018-12-05
    19
  • Li Yao
    如果能举一个实际的应用场景就更好了,这篇看不太懂用途
    2018-11-16
    10
  • 丁香与茉莉
    http://www.flysnow.org/2017/05/12/go-in-action-go-context.html
    2018-11-30
    7
  • 勉才
    context 树不难理解,context.Background 是根节点,但它是个空的根节点,然后通过它我们可以创建出自己的 context 节点,在这个节点之下又可以创建出新的 context 节点。看了 context 的实现,其实它就是通过一个 channel 来实现,cancel() 就是关闭该管道,context.Done() 在 channel 关闭后,会返回。替我们造的轮子主要实现两个功能:1. 加锁,实现线程安全;2. cancel() 会将本节点,及子节点的 channel 都关闭。
    2019-01-01
    6
  • 郝林
    我看不少读者都说写一篇难理解。可能的确如此,因为我假设你们已对context包有基本的了解。

    不过没关系,你们有此方面的任何问题都可以通过以下三个途径与我讨论:

    1. 直接在专栏文章下留言;
    2. 在 GoHackers 微信群或者 BearyChat 中的 GoHackers 团队空间里艾特我;
    3. 在知识星球中的 GoHackers VIP 社群里向我提问。
    2019-04-17
    2
    4
  • Timo
    Context 使用原则
    不要把Context放在结构体中,要以参数的方式传递
    以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
    给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO
    Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
    Context是线程安全的,可以放心的在多个goroutine中传递
    2019-05-28
    3
  • 憶海拾貝
    服务间调用通常要传递上下文数据,用带值Context在每个函数间传递是一种方式,但从Python过来的我觉得这对代码侵入性蛮大的。请问go中还有什么更好的办法传递上下文数据呢?
    2018-11-15
    2
  • 山分子
    cotext.backround()和cotext.todo()有什么区别

    作者回复: 很明显,context.Background()返回的是全局的上下文根(我在文章中多次提到),context.TODO()返回的是空的上下文(表明应用的不确定性)。

    2019-04-09
    1
    1
  • HOPE
    老师,请教个问题。我运行了多个goroutine,每个goroutine有不同的配置。我现在想修改运行时的goroutine的配置,使用context可以实现吗?或者可以用什么办法实现?
    2018-11-12
    1
  • 属鱼
    深度优先,看func (c *cancelCtx) cancel(removeFromParent bool, err error)方法的源代码。
    2018-10-25
    1
  • Shawn
    看代码是深度优先,但是我自己写了demo,顺序是乱的,求老师讲解

    作者回复: 打印出来的顺序不定是正常的,因为goroutine会被实时调度啊,打印出来的顺序不一定就是真实顺序。每填语句执行完都可能被调度。

    2018-10-25
    1
  • 虢国技匠
    对着源码进行二刷
    2019-11-27
  • Geek_f39659
    根据这句话:“A great mental model of using Context is that it should flow through your program. Imagine a river or running water. This generally means that you don’t want to store it somewhere like in a struct. Nor do you want to keep it around any more than strictly needed. Context should be an interface that is passed from function to function down your call stack, augmented as needed.”

    感觉上Context设计上更像是一个运行时的动态概念,它更像是代表传统call stack 层层镶套外加分叉关系的那颗树。代表着运行时态的调用树。所以它最好就是只存在于函数体的闭包之中,大家口口相传,“传男不传女”。“因为我调用你,所以我才把这个传给你,你可以传给你的子孙,但不要告诉别人!”。所以最好不要把它保存起来让旁人有机会看得到或用到。

    楼上有人提到这种风格的入侵性,我能理解你的感觉。但以我以前玩Node.js中的cls (continuation local storage)踩过的那些坑来看,我宁愿两害相权取其轻。这种入侵性至少是可控的,显式的。同步编程的世界我们只需要TLS(Thread local storage)就好了,但对应的异步编程的世界里玩cls很难搞的。在我来看,Context显然是踩过那些坑的老鸟搞出来的。
    2019-11-08
  • moooofly
    “它会向它的所有子值(或者说子节点)传达撤销信号。这些子值会如法炮制,把撤销信号继续传播下去。最后,这个 Context 值会断开它与其父值之间的关联。”--这里有一个问题,我能理解,当在这个上下文树上的某个 node 上触发 cancel 信号时,以该 node 为根的子上下文树会从原来的树上断开;而文中又提到“撤销信号在被传播时,若遇到它们(调用 context.WithValue 函数得到的 Context 值)则会直接跨过” ,那么,这些被“跨过”的 node ,在上面说的子上下文树断开的过程里,是一起断开了?还是仍旧会和更上层的 node 节点有关联?

    作者回复: 逻辑上会一起断开的。但由于 value context 本身不会去传递信号,所以实质上不用做断开操作。这几种 context 所起到的作用是不同的,所以有些专属的操作只会在对应的 context 上做。不过与它们临近的其他种类的 context 会随之联动。你看下源码就清楚了,它们不是完全“链接”在一起的,有的会紧密嵌套在一起,所以有的操作可以很自然地进行跨越式处理。

    2019-09-30
    1
  • 思维
    实际使用中 http.ReverseProxy经常会报 proxy error:context canceled 请问老师有哪些原因可能导致这个问题

    作者回复: 你可以说的具体一些。

    2019-09-02
  • 茴香根
    留言区很多人说Context 是深度优先,但是我在想每个goroutine 被调用的顺序都是不确定的,因此在编写goroutine 代码时,实际的撤销响应不能假定其父或子context 所在的goroutine一定先或者后结束。

    作者回复: 是的,这涉及到两个方面,需要综合起来看。

    2019-07-25
  • 闫飞
    繁衍一词的翻译有些生硬,是否能换一个好理解一些的中文词汇

    作者回复: 翻译的词?这是我找到的一个比较贴切的词。把 Context 比喻成可以繁衍后代的生物不会更容易理解一些么?

    2019-07-16
  • 公众号「后端进阶」
    context包其实相当于Java的同步工具类,比如cancelFunc其实就是一个countDownLatch

    作者回复: 如果我没记错的话,还是不一样的。

    2019-07-04
  • 糊李糊涂
    mark
    2019-06-26
收起评论
26
返回
顶部