为什么当 annoymouse 函数返回时 defer 语句的工作方式不同

这里我声明了一个推迟trace1的函数


func TestDeferFunc(t *testing.T) {

    fmt.Println("start", time.Now())

    defer trace1()

    time.Sleep(3 * time.Second)

}


func trace1() {

    startTime := time.Now()

    fmt.Println("end time: ", startTime)

    fmt.Println("execute time: ", time.Since(startTime))

}

运行后go test -run=^TestDeferFunc$,下面是我得到的


start 2019-11-26 12:50:59.59489797 +0800 CST m=+0.000202866

end time:  2019-11-26 12:51:02.595090951 +0800 CST m=+3.000395880

execute time:  49.065µs

然而,当我推迟另一个 annoymouse 函数时,事情发生了变化


func TestDeferFunc(t *testing.T) {

    fmt.Println("start", time.Now())

    defer trace2()()

    time.Sleep(3 * time.Second)

}


func trace2() func() {

    startTime := time.Now()

    fmt.Println("end time: ", startTime)

    fmt.Println("execute time: ", time.Since(startTime))

    return func() {

        fmt.Println("zzz")

    }

}

下面是go test结果


start 2019-11-26 12:52:58.318472958 +0800 CST m=+0.000197852

end time:  2019-11-26 12:52:58.318554368 +0800 CST m=+0.000279262

execute time:  4.853µs

zzz

有人可以帮我吗!谢谢


慕哥9229398
浏览 81回答 2
2回答

三国纷争

这是因为defer语句仅defer评估函数调用 - 并且函数调用在defer执行时评估。根据文档:每次执行“defer”语句时,调用的函数值和参数都会照常评估并重新保存,但不会调用实际函数。相反,延迟函数会在周围函数返回之前立即调用,调用顺序与延迟函数相反。也就是说,如果周围函数通过显式 return 语句返回,则延迟函数将在该 return 语句设置任何结果参数之后但在函数返回到其调用者之前执行。如果延迟函数值求值为 nil,则在调用该函数时(而不是执行“defer”语句时)会发生执行混乱。您的代码defer trace2()()本质上相当于f := trace2(); defer f(). 因此trace2立即被评估(并因此被调用)。因此,为了实现您可能想要的(跟踪时间),您可以像这样defer trace3()()使用:trace3()func trace3() func() {    startTime := time.Now()    return func() {        fmt.Println("end time: ", time.Now())        fmt.Println("execute time: ", time.Since(startTime))    }}

哆啦的时光机

使用 defer 跟踪时间的方法是将开始时间传递给 deferred 函数:func trace1(startTime time.Time) {    fmt.Println("start", startTime)    fmt.Println("end time: ", time.Now())    fmt.Println("execute time: ", time.Since(startTime))}func TestDeferFunc(t *testing.T) {    defer trace1(time.Now())    time.Sleep(3 * time.Second)}传递的参数会立即执行,但实际的函数调用会推迟到最后。如果您想更明确地了解执行的内容和延迟的内容,可以将整个内容包装在一个函数中,以便确保内部的所有内容都在最后执行defer func() {    ..... code ....}()
打开App,查看更多内容
随时随地看视频慕课网APP