为了账号安全,请及时绑定邮箱和手机立即绑定

多个延迟与延迟匿名函数

多个延迟与延迟匿名函数

Go
慕姐8265434 2021-11-08 14:48:27
发出defer依赖于顺序的多个语句,或者延迟打包逻辑的匿名函数是否更安全或更惯用?例子:defer os.Remove(tempFile.Name())defer tempFile.Close()在上述情况下,语法最少,但延迟的顺序与要执行的逻辑相反。在下面的情况下,有更多行,更多“语法”,但逻辑顺序更自然:defer func() {    tempFile.Close()    os.Remove(tempFile.Name())}()使用哪一种?
查看完整描述

2 回答

?
心有法竹

TA贡献1866条经验 获得超5个赞

在此示例中,匿名函数更易于阅读,尤其是在添加错误处理后。


f, err := ioutil.TempFile("", "prefix")

if err != nil {

  log.Println("creating temp file:", err)

  return

}

defer func() {

  err := f.Close()

  if err != nil {

    log.Println("close:", err)

  }

  err = os.Remove(f.Name())

  if err != nil {

    log.Println("remove:", err)

  }

}()

如果你有多个资源,那么多个defers 通常是合适的。


查看完整回答
反对 回复 2021-11-08
?
海绵宝宝撒

TA贡献1809条经验 获得超8个赞

正如罗斯·莱特(Ross Light)的回答所说:


如果你有多个资源,那么多个 defer 通常是合适的。


2019 年 4 月:但在这种情况下,请考虑 Go 1.13(2019 年第四季度),因为它确实集成了go 问题 14939的修复程序:“运行时:延迟很慢”和go 问题 6980:“cmd/compile:在堆栈帧中分配一些延迟”


请参阅Go CL 171758:“cmd/compile,runtime:在堆栈上分配延迟记录”


当 defer 在函数体中最多执行一次时,我们可以在堆栈上而不是在堆上为其分配 defer 记录。


这应该会使像这样的延迟(非常常见)更快。


此优化适用于 cmd/go 二进制文件中 370 个静态延迟站点中的 363 个。


name     old time/op  new time/op  delta

Defer-4  52.2ns ± 5%  36.2ns ± 3%  -30.70%  (p=0.000 n=10+10)

2019 年 10 月(几周前发布了 Go 1.13)


这在CL 190098 中得到了证实 (Brad Fitzpatrick):


延期声明的费用 [ go test -run NONE -bench BenchmarkDefer$ runtime]


With normal (stack-allocated) defers only:         35.4  ns/op

With open-coded defers:                             5.6  ns/op

Cost of function call alone (remove defer keyword): 4.4  ns/op

但达米安·格里斯基补充道:


Defer 变得更便宜,但 panic/recover 更昂贵。


Cost of defer: 34ns -> 6ns.

Cost of panic/recover: 62ns -> 255ns

这不是一个糟糕的权衡。


换句话说,虽然使用多个 defer 可能是惯用的,但这种做法受到性能成本的阻碍,而 Go 1.13+ 不再关注性能成本。

(如Paschalis的博客文章“什么是延迟?你能跑多少? ”的说明)


如果延迟(在应该执行函数调用的地方,而不管代码流如何),这将具有实际用途。


然而,John Refior指出,这defer是同步的:


实际上 defer 是在函数退出前立即执行的。

它是同步发生的,所以调用者等待 defer 完成。


因此,即使您现在可以有多个 defer,也要确保它们很快,或者,正如 John 所说:


幸运的是,将 goroutine 包装在 a 中很容易defer,为我们提供了我们想要的流量控制和计时,而不会延迟调用者:


func Handler(w http.ResponseWriter, r *http.Request) {

    log.Println("Entered Handler")

    defer func() {

        go func() {

            time.Sleep(5 * time.Second)

            log.Println("Exiting goroutine")

        }()

        log.Println("Exiting defer")

    }()

}

defers 通常用于锁定互斥锁,或关闭连接或文件描述符,它们所做的工作很快,或者我们希望它在调用者继续之前完成。


但是,当您执行客户端不需要在 HTTP 处理程序结束时等待的缓慢工作时,使调用异步可以显着改善用户体验。


查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 199 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信