我目前正在将我们的代码从全局符号包迁移到 mongo-driver,不确定我应该在哪里使用context.TODO()
和context.Background()
,这真的很令人困惑,我知道它都返回非 nil 空,所以我应该在主函数和init()
函数中使用context.Background()
吗?并在其他地方使用context.TODO()
?谁能帮忙?
尝试检查我应该使用哪个参数context.TODO()
或context.Background()
.
检查他们的文档:
context.Background()
:
func Background() Context
背景返回一个非 nil 的空上下文。它永远不会被取消,没有值,也没有截止日期。它通常由 main 函数、初始化和测试使用,并用作传入请求的顶级上下文。
context.TODO()
:
func TODO() Context
TODO 返回一个非 nil 的空上下文。代码应使用上下文。当不清楚要使用哪个上下文或尚不可用时(因为周围的函数尚未扩展为接受上下文参数),则执行 TODO。
根据文档,当您需要一个上下文但您还没有上下文并且不知道该使用什么时,请使用context.TODO()
.这正是您的情况,使用context.TODO()
正确记录这一点。此外,静态分析工具和 IDE 可能会提供支持发现这些上下文,并在以后向您发出警告以解决此问题。
另请注意,如果在必须使用 mongo 驱动程序时确实有上下文,请考虑使用该上下文,或从中派生一个新上下文。
例如,如果您正在编写一个需要查询某些文档的 HTTP 处理程序,则 HTTP 请求http.Request
已经有一个可以使用Request.Context()
访问的上下文。这是调用需要上下文的其他 API 函数时要使用的主要候选项。
你可能会问,为什么?因为当 HTTP 客户端放弃/中止请求时,请求的上下文被取消,这意味着无论你做什么,客户端都不会收到它,所以例如,如果 HTTP 处理程序只提供信息,你不妨中止生成它,节省一些资源。因此,如果在执行MongoDB查询时取消了请求上下文,您也可以取消查询,因为无论如何您都不需要(不会处理)结果。取消传递给查询执行的上下文(例如Collection.Find()
)是告诉MongoDB服务器尽可能取消查询的方法(因为您不需要结果)。
因此,在这种情况下使用请求上下文可以在HTTP服务器和MongoDB服务器上节省一些CPU时间和内存。
另一个例子:假设你必须在10秒内产生HTTP响应(可能是平台限制),在此期间你必须执行MongoDB操作,并且你必须对结果进行后处理,这需要4秒。在这种情况下,如果 MongoDB 操作花费的时间超过 6 秒,您将无法完成后处理以适应 10 秒的限制。因此,如果MongoDB操作在6秒内未完成,则不妨取消该操作。
解决此问题的一种简单方法是派生一个超时为 6 秒的上下文:
ctx, cancel := context.WithTimeout(r.Context(), 6 * time.Second)
defer cancel()
// ctx automatically times out after 6 seconds
curs, err := c.Find(ctx, bson.M{"some": "filter"})
在此示例中,ctx
将在 6 秒后超时,因此如果c.Find()
操作未完成,传递的ctx
将发出可以取消的信号(在这种情况下将返回错误)。由于ctx
是从r.Context()
派生的,如果请求的上下文更早被取消(例如HTTP客户端中止),ctx
也会被取消。
现在,假设您不是在编写 HTTP 处理程序,而是在编写一些其他代码(独立应用程序、后台工作程序等),您可能没有上下文,但您有不想等待超过 30 秒的查询的标准。这是一个典型的示例,您可以在其中使用 30 秒超时从context.Background()
派生上下文:
ctx, cancel := context.WithTimeout(context.Background(), 30 * time.Second)
defer cancel()
// ctx automatically times out after 30 seconds
curs, err := c.Find(ctx, bson.M{"some": "filter"})
如果查询在 30 秒内完成,一切正常,您可以使用结果。否则,上下文将在 30 秒后取消,因此c.Find()
也将(可能)返回错误。
context.TODO
:
代码应使用上下文。当不清楚要使用哪个上下文或尚不可用时(因为周围的函数尚未扩展为接受上下文参数),则执行 TODO。
context.Background
:
背景返回一个非 nil 的空上下文。它永远不会被取消,没有值,也没有截止日期。它通常由 main 函数、初始化和测试使用,并用作传入请求的顶级上下文。
TODO
是一个占位符,用于明确代码将来应使用更合适的上下文进行更新,但尚不可用。 当不需要此类指示并且您只需要一个没有值或取消的空根上下文时,将使用Background
。它们返回的值是相同的;唯一的区别是语义,因为人们可能会扫描代码以查找TODO
上下文(或使用 linter 执行此操作)作为需要更新/寻址的地方。