Go:Context 和传播取消

context 包[1]在 Go 1.7 中引入,它为我们提供了一种在应用程序中处理 context 的方法。这些 context 可以为取消任务或定义超时提供帮助。通过 context 传播请求的值也很有用,但对于本文,我们将重点关注 context 的取消功能。

本文是 Go语言中文网组织的 GCTT 翻译,发布在 Go语言中文网公众号,转载请联系我们授权。

默认的 contexts

Go 的 context 包基于 TODO 或者 Background 来构建 context。

var (
   background = new(emptyCtx)
   todo       = new(emptyCtx)
)

func Background() Context {
   return background
}

func TODO() Context {
   return todo
}

我们可以看到,它们都是空的 context。这是简单的 context,永远不会被取消,也不会带任何值。

你可以将 background context 作为主 context,并将其派生出新的 context。基于这些,你不应直接在包中使用 context;它应该在你的主函数中使用。如果要使用 net/http 包构建服务,则主 context 将由请求提供:

net/http/request.go
func (r *Request) Context() context.Context {
   if r.ctx != nil {
      return r.ctx
   }
   return context.Background()
}

如果你在自己的包中工作并且没有任何可用的 context,在这种情况下你应该使用 TODO context。通常,或者如果你对必须使用的 context 有任何疑问,可以使用 TODO context。现在我们知道了主 context,让我们看看它是如何派生子 context 的。

Contexts 树

父 context 派生出的子 context 会在在其内部结构中创建一个和父 context 之间的联系:

type cancelCtx struct {
   Context

   mu       sync.Mutex
   done     chan struct{}
   children map[canceler]struct{}
   err      error
}

children 字段跟踪以此 context 创建的所有子项,而 Context 指向创建当前项的 context。

以下是创建一些 context 和子 context 的示例:

Image

每个 context 都相互链接,如果我们取消 “C” context,所有它的孩子也将被取消。Go 会对它的子 context 进行循环逐个取消:

context/context.go
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
   [...]
   for child := range c.children {
      child.cancel(false, err)
   }
   [...]
}

取消结束,将不会通知父 context。如果我们取消 C1,它只会通知 C11 和 C12:

Image

这种取消传播允许我们定义更高级的例子,这些例子可以帮助我们根据主 context 处理多个/繁重的工作。

取消传播

让我们通过 Goroutine A 和 B 来展示一个取消的例子,它们将并行运行,因为拥有共同的 context ,当一个发生错误取消时,另外一个也会被取消:

Image

如果没有任何错误发生,每个过程都将正常运行。我在每个任务上添加了一条跟踪,这样我们就可以看到一棵树:

A - 100ms
B - 200ms
    -> A1 - 100ms
        -> A11 - 50ms
    -> B1 - 100ms
        -> A12 - 300ms
    -> B2 - 100ms
        -> B21 - 150ms

每项任务都执行得很好。现在,让我们尝试让 A11 模拟出错误:

A - 100ms
    -> A1 - 100ms
B - 200ms
        -> A11 - error
        -> A12 - cancelled
    -> B1 - 100ms
    -> B2 - cancelled
    -> B21 - cancelled

我们可以看到,当 B2 和 B21 被取消的同时,A12 被中断,以避免做出不必要的处理(译者注:B2 B21 的取消不是因为 A12 中断,应该是想表达并发安全的意思):

Image

我们可以在这里看到 context 对于多个 Goroutine 是线程安全的。实际上,有可能是因为我们之前在结构中看到的 mutex,它保证了对 context 的并发安全。

context 泄漏

正如我们在内部结构中看到的那样,当前 context 在 Context 属性中保持其父级的链接,而父级将当前 context 保留在 children 属性中。对 cancel 函数的调用将把当前 context 中的子项清除并删除与父项的链接:

func (c *cancelCtx) cancel(removeFromParent bool, err error) {
   [...]
   c.children = nil

   if removeFromParent {
      removeChild(c.Context, c)
   }
}

如果未调用 cancel 函数,则主 context 将始终保持与它创建的 context 的链接,从而导致可能的内存泄漏。

可以用 go vet 命令来检查是否泄漏,它将对可能的泄漏抛出警告:

the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak

总结

context 包还有另外两个利用 cancel 函数的函数:WithTimeoutWithDeadline。在定义的超时/截止时间后,它们都会自动触发 cancel 函数。

context 包还提供了一个 WithValue 的方法,它允许我们在 context 中存储任何对键/值。此功能受到争议,因为它不提供明确的类型控制,可能导致糟糕的编程习惯。如果你想了解 WithValue 的更多信息,我建议你阅读Jack Lindamood 关于 context 值的文章[2]。


via: https://medium.com/@blanchon.vincent/go-context-and-cancellation-by-propagation-7a808bbc889c

作者:Vincent Blanchon[3]译者:咔叽咔叽[4]校对:zhoudingding[5]

本文由 GCTT[6] 原创编译,Go 中文网[7] 荣誉推出,发布在 Go语言中文网公众号,转载请联系我们授权。

参考资料

[1]

context 包: https://blog.golang.org/context

[2]

Jack Lindamood 关于 context 值的文章: https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39

[3]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[4]

咔叽咔叽: https://github.com/watermelo

[5]

zhoudingding: https://github.com/dingdingzhou

[6]

GCTT: https://github.com/studygolang/GCTT

[7]

Go 中文网: https://studygolang.com/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`boost::asio::ssl::context`是Boost.Asio库中的一个类,用于处理SSL/TLS(Secure Sockets Layer/Transport Layer Security)通信。它提供了高级的接口,以便在Asio网络编程中设置和管理SSL连接。`context`类包含了多种选项,用于配置SSL上下文,确保安全的通信。以下是一些关键的选项: 1. **mode**: 设置SSL模式,例如`boost::asio::ssl::context::client`(客户端)或`boost::asio::ssl::context::server`(服务器)。 2. **cipher_list**: 可以指定允许使用的加密算法列表,如"TLSv1+AES256+SHA256"。 3. **protocol_version**: 设置支持的SSL/TLS协议版本,如`tlsv1_2`、`tlsv1_3`。 4. **cert_chain**: 加载服务器证书,通常包括私钥,确保身份验证。 5. **private_key**: 加载用于加密的私钥。 6. **trust_store**: 信任存储,包含CA(证书颁发机构)证书,用于验证对方的证书。 7. **options**: 可以调整其他选项,如允许匿名身份、要求服务器证明等。 8. **alpn_negotiation**: 设置Application-Layer Protocol Negotiation (ALPN)支持,用于选择客户端支持的协议。 9. **session_cache**: 控制SSL会话缓存,可以优化性能。 10. **verify_mode**: 客户端验证服务器的方式,如`verify_none`、`verify_peer`、`verify_client_once`等。 11. **next_protocols**: 服务器上如何处理ALPN协商,可能是一个字符串列表。 每个选项都有其特定的意义和用法,使用时需根据实际需求进行配置。如果你对某个选项的具体用法有疑问,可以告诉我,我会进一步解释。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值