使用context实现超时控制
在Go语言中,通过使用context包,可以很方便地实现超时控制,其可以用来控制goroutine的取消和超时。
1. context的基础知识
在了解context如何进行超时控制前,需要了解context的基础知识。
1.1 context的概念
context是Go语言中提供的一个用来在多个goroutine之间传递上下文(context)的标准库。
官方定义:包context定义了Context类型,该类型跟踪一个请求的上下文信息,例如:截止期限、取消信号或请求的元数据。
使用context可以避免使用全局变量和锁的方式进行通信,从而实现并发安全的代码。
1.2 context的使用
context类型提供了以下方法和功能:
- WithCancel(parent Context) (ctx Context, cancel CancelFunc):创建一个可取消的子context。
- WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc):创建一个有截止期限的子context。
- WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc):创建一个有超时控制的子context。
- WithValue(parent Context, key interface{}, val interface{}) Context:创建一个携带值的子context。
1.3 context的传递
context可以在不同goroutine之间传递。
传递的方式包括context的传入和传出。
在函数的参数列表中,可以传入context参数,将当前context作为子context,传递给子函数。
func MyFunc(ctx context.Context){
// ...
}
如果在子函数中需要获取父context,可以通过context的Value()方法,获取传递的值。
2. 实现超时控制
go通过context来实现操作的超时控制。主要是通过ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)这种方式实现上下文,并利用上下文的超时机制来控制操作时间。
2.1 context.WithTimeout()
有了context包,我们可以很容易地使用`WithTimeout(parent Context, timeout time.Duration)`来实现超时控制。使用方式如下:
func main() {
// 父context
ctx := context.Background()
// 创建一个在5秒后超时的子context
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// 通过子context创建的协程(使用select)
go func() {
select {
case <- ctx.Done():
fmt.Println("耗时较长的操作已超时,需要退出")
return
default:
fmt.Println("long operation...")
time.Sleep(10*time.Second) // 模拟
fmt.Println("long operation complete.")
}
}()
}
在上述代码中,ctx表示父context,使用`context.Background()`创建。ctx, cancel := context.WithTimeout(ctx, 5*time.Second)表示创建了一个在5秒后会超时的子context。同时使用defer延迟执行cancel(),以确保在主函数返回之前,协程已被正确释放和停止。
2.2 context.WithDeadline()
context.WithDeadline()方法和context.WithTimeout()类似,不过其功能是设置一个截止时间,而不是一个超时时间。
2.3 context.WithCancel()
context.WithCancel()方法用来创建一个可取消的子context。
在使用了该方法创建的子context中,如果父context被取消,则子context也会被取消。
下面是一个使用context.WithCancel()方法的示例:
func main() {
// 父context
ctx := context.Background()
// 创建一个可取消的子context
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// 在子协程中等待终止信号
go func() {
select {
case <- time.After(5*time.Second):
fmt.Println("完成。")
case <- ctx.Done():
fmt.Println("已经取消。")
}
}()
// 运行10秒
time.Sleep(10*time.Second)
// 取消协程
cancel()
}
在上述代码中,首先创建了一个可取消的子context ctx。然后启动了一个goroutine,其中包含了一个select,该select语句在定时器超时或收到取消信号时退出。
在主函数中,运行了10秒后,调用了cancel(),通知goroutine需要退出,并在另外一个select中检测是否已经退出。
3. 关于协程超时和取消的场景选择
3.1 超时控制
对于耗时较长的操作,比如网络io、文件io、进程调用等,推荐使用context.WithTimeout()方法实现超时控制。这样可以保证操作在规定的时间内完成。
3.2 取消操作
在执行一个可以通过用户命令取消的操作时,比如计算密集型的操作,推荐使用context.WithCancel()方法实现。
4. 总结
本文主要介绍了Go语言中使用context包实现超时控制的方法和场景选择。context包提供了超时、截止期限、值传递等多种功能,为Go语言的并发编程提供了很好的支持和帮助。能够熟练使用context包实现超时控制,对Go语言并发编程的实现有很大的帮助。