前言

日常开发中我们大概率会遇到超时控制的场景,比如一个批量耗时任务、网络请求等;一个良好的超时控制可以有效的避免一些问题(比如 goroutine 泄露、资源不释放等)。

Timer

在 go 中实现超时控制的方法非常简单,首先第一种方案是 Time.After(d Duration)

func main() {
fmt.Println(time.Now())
x := <-time.After(3 * time.Second)
fmt.Println(x)
}

output:

2021-10-27 23:06:04.304596 +0800 CST m=+0.000085653
2021-10-27 23:06:07.306311 +0800 CST m=+3.001711390

time.After() 会返回一个 Channel,该 Channel 会在延时 d 段时间后写入数据。

有了这个特性就可以实现一些异步控制超时的场景:

func main() {
ch := make(chan struct{}, 1)
go func() {
fmt.Println("do something...")
time.Sleep(4*time.Second)
ch<- struct{}{}
}() select {
case <-ch:
fmt.Println("done")
case <-time.After(3*time.Second):
fmt.Println("timeout")
}
}

这里假设有一个 goroutine 在跑一个耗时任务,利用 select 有一个 channel 获取到数据便退出的特性,当 goroutine 没有在有限时间内完成任务时,主 goroutine 便会退出,也就达到了超时的目的。

output:

do something...
timeout

timer.After 取消,同时 Channel 发出消息,也可以关闭通道等通知方式。

注意 Channel 最好是有大小,防止阻塞 goroutine ,导致泄露。

Context

第二种方案是利用 context,go 的 context 功能强大;



利用 context.WithTimeout() 方法会返回一个具有超时功能的上下文。

	ch := make(chan string)
timeout, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go func() {
time.Sleep(time.Second * 4) ch <- "done"
}() select {
case res := <-ch:
fmt.Println(res)
case <-timeout.Done():
fmt.Println("timout", timeout.Err())
}

同样的用法,contextDone() 函数会返回一个 channel,该 channel 会在当前工作完成或者是上下文取消生效。

timout context deadline exceeded

通过 timeout.Err() 也能知道当前 context 关闭的原因。

goroutine 传递 context

使用 context 还有一个好处是,可以利用其天然在多个 goroutine 中传递的特性,让所有传递了该 context 的 goroutine 同时接收到取消通知,这点在多 go 中应用非常广泛。

func main() {
total := 12
var num int32
log.Println("begin")
ctx, cancelFunc := context.WithTimeout(context.Background(), 3*time.Second)
for i := 0; i < total; i++ {
go func() {
//time.Sleep(3 * time.Second)
atomic.AddInt32(&num, 1)
if atomic.LoadInt32(&num) == 10 {
cancelFunc()
}
}()
}
for i := 0; i < 5; i++ {
go func() { select {
case <-ctx.Done():
log.Println("ctx1 done", ctx.Err())
} for i := 0; i < 2; i++ {
go func() {
select {
case <-ctx.Done():
log.Println("ctx2 done", ctx.Err())
}
}()
} }()
} time.Sleep(time.Second*5)
log.Println("end", ctx.Err())
fmt.Printf("执行完毕 %v", num)
}

在以上例子中,无论 goroutine 嵌套了多少层,都是可以在 context 取消时获得消息(当然前提是 context 得传递走)

某些特殊情况需要提前取消 context 时,也可以手动调用 cancelFunc() 函数。

Gin 中的案例

Gin 提供的 Shutdown(ctx) 函数也充分使用了 context

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")

比如以上代码便是超时等待 10s 进行 Gin 的资源释放,实现的原理也和上文的例子相同。

总结

因为写 go 的时间不长,所以自己写了一个练手的项目:一个接口压力测试工具。



其中一个很常见的需求就是压测 N 秒后退出,这里正好就应用到了相关知识点,同样是初学 go 的小伙伴可以参考。

https://github.com/crossoverJie/ptg/blob/d0781fcb5551281cf6d90a86b70130149e1525a6/duration.go#L41

Go 里的超时控制的更多相关文章

  1. .net Session 超时控制

    webconfig里明明设置了timeout是很大的数值了,可是session的有效性仍然无法维持一小时. 查了一下,主要是说 mode="InProc" 的话,session是放 ...

  2. ExtJS4.2.1与Spring MVC实现Session超时控制

    假设你的项目使用ExtJS作为表现层.你会发现,SESSION超时控制将是一个问题. 本文将就自己的经验.来解决这一问题.当然,解决这个问题并不是仅仅有一种方法,我仅仅是提出我的方法. 首先.做超时控 ...

  3. Go 采用 time.After 实现超时控制

    场景: 假设业务中需调用服务接口A,要求超时时间为5秒,那么如何优雅.简洁的实现呢? 我们可以采用select+time.After的方式,十分简单适用的实现. time.After()表示time. ...

  4. 一文搞懂如何实现 Go 超时控制

    为什么需要超时控制? 请求时间过长,用户侧可能已经离开本页面了,服务端还在消耗资源处理,得到的结果没有意义 过长时间的服务端处理会占用过多资源,导致并发能力下降,甚至出现不可用事故 Go 超时控制必要 ...

  5. 【小程序分享篇 一 】开发了个JAVA小程序, 用于清除内存卡或者U盘里的垃圾文件非常有用

    有一种场景, 手机内存卡空间被用光了,但又不知道哪个文件占用了太大,一个个文件夹去找又太麻烦,所以我开发了个小程序把手机所有文件(包括路径下所有层次子文件夹下的文件)进行一个排序,这样你就可以找出哪个 ...

  6. 在.NET Core 里使用 BouncyCastle 的DES加密算法

    .NET Core上面的DES等加密算法要等到1.2 才支持,我们可是急需这个算法的支持,文章<使用 JavaScriptService 在.NET Core 里实现DES加密算法>需要用 ...

  7. 使用 JavaScriptService 在.NET Core 里实现DES加密算法

    文章<ASP.NET Core love JavaScript>和<跨平台的 NodeJS 组件解决 .NetCore 不支持 System.Drawing图形功能的若干问题> ...

  8. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  9. .NET里简易实现AOP

    .NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...

随机推荐

  1. msf宏钓鱼

    kali下载python脚本,生成rtf文件: 下载脚本:git clone https://github.com/bhdresh/CVE-2017-8759.git 生成rtf文件: python ...

  2. Ubuntu管理员手册

    hostname cat /etc/hosts apt-get install iproute2 iproute2-doc ip a ps -aux

  3. Spring Boot 2.x 之 H2 数据库

    1. Spring Boot下H2数据库的常用配置项 # 指定数据库的类型 spring.datasource.platform=h2 # 数据库连接地址(文件模式) ## AUTO_SERVER=T ...

  4. 对javaEE Tutorial上hello2的源码分析详解

    首先: java EE 上的hello2项目是一个部署在glass fish上的开发源码的java web项目,在终端通过命令行使用maven进行打包成.war文件,最后部署到相关的glass fis ...

  5. 安装VM-TOOLS,解压tar包时提示目录磁盘空间不足

    在虚拟机里安装了ubuntu-18.04.4-desktop-amd64,安装VM-TOOLS,解压tar包时提示目录磁盘空间不足. 解决方法一: 打开terminal,输入:sudo apt ins ...

  6. dubbo微服务架构

    架构 节点角色说明 调用关系说明 服务容器负责启动,加载,运行服务提供者. 服务提供者在启动时,向注册中心注册自己提供的服务. 服务消费者在启动时,向注册中心订阅自己所需的服务. 注册中心返回服务提供 ...

  7. 彻底关闭Windows自动更新

    win+r--输入services.msc(服务管理窗口)停止windows update服务并禁用同时在恢复里,改为无操作 win + r --输入gpedit.msc(本地组策略编辑器)家庭版没有 ...

  8. Fortran学习笔记:01 基本格式与变量声明

    Fortran学习笔记目录 01 基本格式与变量声明 格式 固定格式(Fixed Format):Fortran77 程序需要满足一种特定的格式要求,具体形式参考教材 自由格式(Free Format ...

  9. The art of multipropcessor programming 读书笔记-硬件基础2

    本系列是 The art of multipropcessor programming 的读书笔记,在原版图书的基础上,结合 OpenJDK 11 以上的版本的代码进行理解和实现.并根据个人的查资料以 ...

  10. 浅谈机(J)惨(C)技巧——从入门到精通(?)

    JC总是无聊的机房中有意思的一个瞬间... 比如: 这个杰作由 ZSWBWYX 完成 你认为这只是AK-IOI吗?不!注意用户名...也是此人的杰作. 所以,在险恶的机房里,一定要保护好自己的账号. ...