本文翻译自《Canceling in-progress operations》。 你可以使用Go的context.Context
管理正在进行中的操作。Context
是一个标准的Go类型,可用来报告它所代表的整体操作是否已被取消并且不再需要。通过在你的应用程序中跨函数或服务传递context.Context
,可以提前停止工作并返回一个错误。有关Context
的更多信息,请参阅Go并发模式:Context。
例如,你可能希望:
- 结束长时间运行的操作,包括耗时过长的数据库操作。
- 传播来自其他地方的“取消继续工作”请求,例如当客户端关闭连接时。
Go的许多API都采用Context
参数,使你更容易在整个应用程序中使用Context
。
超时后取消正在进行中的数据库操作
你可以使用Context
来设置超时时间或截止时间,在此之后操作将被取消。要派生具有超时时间或截止时间的Context
,请调用Context.WithTimeout
或Context.WithDeadline
函数。
以下示例中的代码派生一个附带超时时间的Context
,并将其传递到sql.DB
的QueryContext
方法中。
func QueryWithTimeout(ctx context.Context) {
// 创建一个附带超时时间的Context。
queryCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// 传入这个附带超时时间的Context和一个查询语句。
rows, err := db.QueryContext(queryCtx, "SELECT * FROM album")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// 处理返回的行数据。
}
当一个context从外部的context派生而来时,由于queryCtx
在本例中是从函数参数ctx
派生的,如果外部的context被取消,那么派生的context也会自动取消。例如,在HTTP服务器中,HTTP.Request.Context
方法返回与请求相关联的context。如果HTTP客户端断开连接或取消HTTP请求(可能使用HTTP/2),则该context将被取消。如果整个HTTP请求被取消,或者查询耗时超过五秒,那么将HTTP请求的context传递给上面的QueryWithTimeout
函数将导致数据库查询操作提前结束。
注意:当你在创建带有超时时间或截止时间的一个新context时,总是应该defer
对这个context的cancel
方法的调用。当包含这个新context的函数退出时,就能释放这个新context所持有的资源。它还将取消queryCtx
,当这个新context的cancel
方法返回时,就不应该再使用queryCtx
。