Go语言defer、select执行逻辑与context相关用法问题咨询
问题1解答
是的,defer cancel()的执行时机确实在末尾select语句执行结束之后。
Go语言中defer语句的执行规则是:在当前所属函数将要返回前,按defer声明的逆序执行。你这段代码里两个defer都声明在末尾select语句之前,select语句本身是阻塞等待ctx.Done()触发的,只有select拿到返回值执行完对应分支逻辑后,函数才会进入返回前的收尾阶段,此时才会依次执行defer cancel()和defer close(message)。
问题2解答
首先可以明确两个结论:
- 这里
ctx.Done()永远不会返回nil - 末尾的
select语句不会发生永久阻塞
原因如下:
- 只有根上下文
context.Background()、context.TODO()的Done()方法才会返回nil,你代码中的ctx是通过context.WithTimeout()生成的子上下文,这类带取消能力的上下文的Done()方法一定会返回一个真实存在的通道,不会是nil。 - 这个子上下文的取消逻辑不需要等你手动调用
cancel()触发:WithTimeout底层会自动启动定时器,5秒超时时间到了之后,底层会自动调用取消函数关闭Done()通道,此时末尾select中的<-ctx.Done()会立刻返回,select执行结束,之后才会走到defer cancel()的逻辑。 - 额外补充:context的取消函数是幂等的,就算已经因为超时自动取消过了,后续
defer再调用cancel()也不会产生任何副作用,符合Go context的最佳实践要求。
内容的提问来源于stack exchange,提问作者Shin-chan Loves Elephants
相关产品推荐
相关产品推荐

