Go - 延迟函数的不一致评估

Go - Inconsistent Evaluation of Deferred Functions

我正在试验 Go,我发现延迟函数有一些意想不到的行为。考虑以下将全局变量递增给定数量的程序。

package main

import "fmt"

var z = 1

func main() {

    defer increaseZ(10)
    defer fmt.Println("z =", increaseZ(20), "Deferred Value 1")
    defer fmt.Println("z =", increaseZ(30), "Deferred Value 2")

    fmt.Println("z =", z, "Main Value")
}

func increaseZ(y int) int {
    z += y
    println("z =", z, "Inside Increase Function")
    return z
}

run in the go playground时,输出:

z = 21 Inside Increase Function
z = 51 Inside Increase Function
z = 61 Inside Increase Function
z = 51 Main Value
z = 51 Deferred Value 2
z = 21 Deferred Value 1

如果我切换延迟函数的顺序,它会产生另一个效果:

defer fmt.Println("z =", increaseZ(20), "Deferred Value 1")
defer fmt.Println("z =", increaseZ(30), "Deferred Value 2")
defer increaseZ(10)

输出:

z = 21 Inside Increase Function
z = 51 Inside Increase Function
z = 51 Main Value
z = 61 Inside Increase Function
z = 51 Deferred Value 2
z = 21 Deferred Value 1

Go 文档指出:

The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

因此 正在评估的参数,可以解释为什么返回的主值是 51 而不是 61,因为 fmt.Println 语句将 increaseZ 作为参数,但是 defer increaseZ(10) 直到主函数 returns 之后才会被调用。

然而,这并不能解释为什么在第一个示例中 increaseZ(10) 在 main 完成之前输出,而在第二个示例中在 main 完成之后输出。

如果有人能帮助我理解这里发生的事情,我将不胜感激,因为这看起来像是滋生了难以诊断的错误的沃土。

我怀疑这是 Go 游乐场的错误。当我在我的机器上编译并 运行 这个程序时,它产生了预期的输出。已提交与此问题相关的 bug report

您的打印目的地不一致。

stdout: fmt.Println

stderr: println

写入相同的打印目的地。

package main

import "fmt"

var z = 1

func main() {

    defer increaseZ(10)
    defer fmt.Println("z =", increaseZ(20), "Deferred Value 1")
    defer fmt.Println("z =", increaseZ(30), "Deferred Value 2")

    fmt.Println("z =", z, "Main Value")
}

func increaseZ(y int) int {
    z += y
    fmt.Println("z =", z, "Inside Increase Function")
    return z
}

输出:

z = 21 Inside Increase Function
z = 51 Inside Increase Function
z = 51 Main Value
z = 51 Deferred Value 2
z = 21 Deferred Value 1
z = 61 Inside Increase Function

或者,

package main

import (
    "fmt"
    "os"
)

var z = 1

func main() {

    defer increaseZ(10)
    defer fmt.Fprintln(os.Stderr, "z =", increaseZ(20), "Deferred Value 1")
    defer fmt.Fprintln(os.Stderr, "z =", increaseZ(30), "Deferred Value 2")

    fmt.Fprintln(os.Stderr, "z =", z, "Main Value")
}

func increaseZ(y int) int {
    z += y
    println("z =", z, "Inside Increase Function")
    return z
}

输出:

z = 21 Inside Increase Function
z = 51 Inside Increase Function
z = 51 Main Value
z = 51 Deferred Value 2
z = 21 Deferred Value 1
z = 61 Inside Increase Function

这与延迟评估无关。是关于印刷的。 println 函数记录的完整性,但不保证完全保留在该语言中。 Stdout 和 Stderr 也按设计在 Playground 上合并为一个流。 如果你到处使用 fmt.Println(...) http://play.golang.org/p/PU3hxHCazA 或明确定义 fmt.Fprintln(os.Stdout, ... http://play.golang.org/p/OQpOQR2vm0 事情会按预期工作。