前言在日常开发中,我们大概率会遇到超时控制的场景,比如一批耗时任务、网络请求等;一个好的超时控制可以有效避免一些问题(比如goroutine泄露,资源未释放等)。Timer在go中实现超时控制的方法很简单。首先,第一个解决方案是Time.After(dDuration):funcmain(){fmt.Println(time.Now())x:=<-time.After(3*time.Second)fmt.Println(x)}output:2021-10-2723:06:04.304596+0800CSTm=+0.0000856532021-10-2723:06:07.306311+0800CSTm=+3.001711390time.AfterChannel()会返回a,Channel延迟d后写入数据。利用这个特性,可以实现一些异步控制超时的场景:funcmain(){ch:=make(chanstruct{},1)gofunc(){fmt.Println("dosomething...")time.Sleep(4*time.Second)ch<-struct{}{}}()select{case<-ch:fmt.Println("done")case<-time.After(3*time.Second):fmt.Println("timeout")}}这里假设有一个goroutine在运行一个耗时任务。使用select,通道具有获取数据后退出的特性。当goroutine在限定时间内没有完成任务时,maingoroutine就会退出,达到了。超时的目的。dosomething...timeouttimer.After被取消,同时Channel发送消息,或者关闭channel等通知方式。请注意,Channel最好有一个大小,以防止阻塞goroutines并导致泄漏。Context方案二是使用context,go的context功能强大;使用context.WithTimeout()方法将返回一个带有超时函数的上下文。ch:=make(chanstring)timeout,cancel:=context.WithTimeout(context.Background(),3*time.Second)defercancel()gofunc(){time.Sleep(time.Second*4)ch<-"完成"}()select{caseres:=<-ch:fmt.Println(res)case<-timeout.Done():fmt.Println("timout",timeout.Err())}用法相同,context的Done了()函数返回一个通道,该通道将在当前作业完成或上下文被取消时生效。timoutcontextdeadlineexceeded也可以通过timeout.Err()知道当前上下文关闭的原因。goroutine使用context传递context的另一个好处是可以利用其天然的传入多个goroutine的特性,使得所有传递context的goroutine都可以同时收到取消通知,这在multi-go中被广泛使用。funcmain(){total:=12varnumint32log.Println("begin")ctx,cancelFunc:=context.WithTimeout(context.Background(),3*time.Second)fori:=0;i
