grpc-go源码剖析五十七之从golang源码的角度,分析一下调用cancel()后,为什么ctx.Done()就不阻塞了?

已发表的技术专栏
0  grpc-go、protobuf、multus-cni 技术专栏 总入口

1  grpc-go 源码剖析与实战  文章目录

2  Protobuf介绍与实战 图文专栏  文章目录

3  multus-cni   文章目录(k8s多网络实现方案)

4  grpc、oauth2、openssl、双向认证、单向认证等专栏文章目录)

本小节我们简单分析一下golang语言中的cancel函数,

为什么调用cancel函数后,ctx.Done()就不再阻塞了呢?

在grpc-go/examples/features/cancellation/client/main.go文件中,以下面的语句作为分析入口:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

cancel是context.WithTimetout返回的函数;

因此,进入WithTimeout方法内部:

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

其中,parent就是context.Background();

进入WithDeadline方法里:

1func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
2if parent == nil {
3panic("cannot create context from nil parent")
4}
5if cur, ok := parent.Deadline(); ok && cur.Before(d) {
6// The current deadline is already sooner than the new one.
7return WithCancel(parent)
8}
9.	c := &timerCtx{
10.		cancelCtx: newCancelCtx(parent),
11.		deadline:  d,
12}
13propagateCancel(parent, c)
14.	dur := time.Until(d)
15if dur <= 0 {
16.		c.cancel(true, DeadlineExceeded) // deadline has already passed
17return c, func() { c.cancel(false, Canceled) }
18}
19.	c.mu.Lock()
20defer c.mu.Unlock()
21if c.err == nil {
22.		c.timer = time.AfterFunc(dur, func() {
23.			c.cancel(true, DeadlineExceeded)
24})
25}
26return c, func() { c.cancel(true, Canceled) }
27}

其实,我们不需要了解这个方法的具体执行逻辑;

观察这个函数,我们会发现,返回的形式,有两种类型

  • 一个是返回WithCancel函数
  • 一个是返回c,以及一个匿名函数func() { c.cancel(XXXX, YYYY) }

其中,WithCancel函数内部,最终返回的也是c,以及一个匿名函数func() { c.cancel(XXXX, YYYY) }

而这个匿名函数,最终就是赋值给cancel的;

当客户端主动调用cancel函数时,其实就是调用这个匿名函数;最终调用的是c.cancel方法,

因此,进入c.cancel方法里:

1func (c *timerCtx) cancel(removeFromParent bool, err error) {
2.	c.cancelCtx.cancel(false, err)
3if removeFromParent {
4// Remove this timerCtx from its parent cancelCtx's children.
5removeChild(c.cancelCtx.Context, c)
6}
7.	c.mu.Lock()
8if c.timer != nil {
9.		c.timer.Stop()
10.		c.timer = nil
11}
12.	c.mu.Unlock()
13}

我们只关心第2行:继续进入cancel方法里:

1func (c *cancelCtx) cancel(removeFromParent bool, err error) {
2//---省略掉不相关代码
3if c.done == nil {
4.	    	c.done = closedchan
5} else {
6close(c.done)
7}
8//---省略掉不相关代码
9}

只关心第6行:执行close(c.done),也就是关闭通道;

我们知道,

一个通道关闭后,还是可以从通道里读取到默认数据的,也就是说c.done通道就会解除阻塞状态;

即grpc-go/stream.go文件中的newClientStream函数里的 “case <- ctx.Done()” 就会解除阻塞,从而触
发客户端流cs调用finish的处理逻辑。

总结:
调用cancel,其实最终调用的是golang内置的函数close,是用来关闭通道的;

从而达到的效果就是:通道解除阻塞了。

下一篇文章
  截止时间相关介绍

点击下面的图片,返回到专栏大纲

gRPC-go源码剖析与实战之专栏大纲

gRPC-go源码剖析与实战感谢

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码二哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值