前言
web服务开发过程中经常遇到我们需要某一个逻辑最长执行多少时间,超过这个时间需要快速返回而不是继续等待,常用的有这么几种方法
方法一
ch := make(chan bool, 1)
timeout := make(chan bool, 1)
// Send a message to our timeout channel after 1s
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()
go func() {
// do something
ch <- true
}()
// Wait for a message, or timeout
select {
case <-ch:
fmt.Println("Read from ch")
case <-timeout:
fmt.Println("Timed out")
}
一句话评价为快速但是资源有浪费,这是最容易想到的方法,但是不足之处在于第一个协程总是需要等待1s,即使我们需要的处理很快就完成,有一定的资源浪费,而且资源没有回收,都需要等待gc。
方法二
ch := make(chan bool, 1)
go func() {
// do something
ch <- true
close(ch)
}()
select {
case <-ch:
fmt.Println("Read from ch")
case <-time.After(1 * time.Second):
fmt.Println("Timed out")
}
在方法一的基础上优化了一些,提前close了ch,这样如果处理很快完成,ch也很块被释放,但是定时器好像还是仍然需要等1s后才可以交给gc去释放。
方法三
ch := make(chan bool, 1)
go func() {
ch <- true
close(ch)
}()
timer := time.NewTimer(1 * time.Second)
defer timer.Stop()
select {
case <-ch:
fmt.Println("Read from ch")
case <-timer.C:
fmt.Println("Timed out")
}
在方法二的基础上继续优化,定时器修改为可以提前终止的方式,这样我们是主动的去释放掉所有临时的资源了,因此推荐这种方法。
综述
最终完整代码见这个,以接口的方式提供。
var ErrTimeout = errors.New("timeout")
func RunWithTimeout(handler func(), timeout time.Duration) error {
done := make(chan bool, 1)
go func() {
handler()
done <- true
close(done)
}()
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case <-timer.C:
return ErrTimeout
case <-done:
return nil
}
}