【问题标题】:How to decide between a context.WithDeadline or a simple timer?如何在 context.WithDeadline 或简单计时器之间做出决定?
【发布时间】:2017-07-10 14:06:18
【问题描述】:

在 Golang 中,我对将contexts 下游传递给其他方法和函数的意图相当陌生。我了解 context 的工作原理、使用方式、保存值的方式、与父级 context 的关系及其行为 - 我只是不明白为什么要使用首先是上下文。

在一个更具体的例子中,这是这个问题的实际原因,在我工作的公司中,我们发现了一些非常长时间运行的查询,这些查询由于边缘情况而经常发生。

考虑到在我们投入时间解决根本原因之前的限制,我们决定采取的一个显而易见的解决方案是终止耗时超过 5 分钟的查询。

运行我们的事务的方法接受最初在 API 调用中启动的 context。这个context 一直传递给事务函数。在那一刻,我找到了 2 个解决方案来终止该查询:

1) 使用新的上下文:

  • 发起一个新的context.WithTimeout(ctx, time.Duration( 5 * time.Minute) )

  • 观察go routine 中的Done 频道,并在有信号时终止交易

  • 如果事务及时成功完成,只需cancel 上下文并按预期提交事务。

2) 使用Timer:

  • 创建一个持续时间为 5 分钟的 Timer
  • 如果时间结束,终止事务
  • 否则,提交事务。

从逻辑上讲,它们是相同的解决方案,但是,何时以及如何决定是使用具有设定截止日期的 context 还是使用旧的 Timer

【问题讨论】:

  • 从 1.8 开始,database/sql 支持使用上下文。我想这可以简化您的决定。

标签: go timer transactions


【解决方案1】:

答案在于context.Contexttime.Timer 如何传递(取消)信号。

context.Context 让您可以通过Context.Done() 方法访问通道当使用它的goroutines 应该终止时,该方法将关闭

time.Timer 使您可以访问Timer.C 结构字段中的通道将在给定时间段后发送值(该值将是当前时间,但不重要在这里)。

在那里,重点突出显示。通道关闭可以被任意数量的 goroutine 观察到,并且无限次。 Spec: Receive operator:

closed 通道上的接收操作始终可以立即进行,在接收到任何先前发送的值后产生元素类型的 zero value

因此,Context 可以用于向任意数量的 goroutine 和位置发出取消信号。 Timer 只能用于向一个目标发出信号,即从其通道接收值的目标。如果多个客户端正在侦听或尝试从其频道接收,则只有一个会幸运地接收到它。

此外,如果您正在使用/处理已经支持/期望context.Context 的代码,那么使用哪一个就不是问题了。 Go 1.8 还添加了more context support。具有上下文支持的database/sql 包已经有significant additions;包括DB.BeginTx()

在事务提交或回滚之前使用提供的上下文。如果上下文被取消,则 sql 包将回滚事务。如果取消提供给 BeginTx 的上下文,Tx.Commit 将返回错误。

这是context.Context的主要用途:跨API边界携带截止日期和信号取消,并且以并发安全的方式完成(因为Context值是不可变的,并且通道对于并发使用也是安全的, 设计上不会发生数据竞争;更多信息:If I am using channels properly should I need to use mutexes?)。

相关博文:

The Go Blog: Go Concurrency Patterns: Context

Pitfalls of context values and how to avoid or mitigate them in Go

Dave Cheney: Context is for cancelation

【讨论】:

  • 我试图了解哪一个应该用于我的用例。我遇到了你的大部分好帖子。你能解释一下to signal cancelation to arbitrary number of goroutines and places吗?在什么场景下,一个应用会有很多 Goroutine?有什么简单的例子吗?
  • @Gibbs 一个简单的例子:你有一个 HTTP 处理程序,它在它的 goroutine 中完成它的工作。它可以执行对数据库的日志记录,并且可以在单独的 goroutine 中同时执行此操作。如果需要中止其工作,该单独的 goroutine 可能会使用/监视请求的上下文。
猜你喜欢
  • 2011-03-04
  • 2017-07-25
  • 2011-05-19
  • 2013-06-13
  • 2016-02-03
  • 1970-01-01
  • 2023-03-17
  • 2013-12-30
  • 2021-01-11
相关资源
最近更新 更多