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

时间:2015-03-28 12:08:01

标签: go deferred-execution

我正在试验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文档说明:

  

延迟调用的参数会立即被评估,但是   在周围函数返回之前不会执行函数调用。

所以正在评估的参数,可以解释为什么返回的Main Value是51而不是61,因为fmt.Println语句将increaseZ作为参数,但是{{1}直到main函数返回后才会被调用。

但是,这并不能解释为什么在第一个例子中defer increaseZ(10)在main完成之前输出,并且在main完成之后在第二个例子中输出。

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

3 个答案:

答案 0 :(得分:2)

您的打印目的地不一致。

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

答案 1 :(得分:2)

不是延期评估。这是关于印刷的。记录println函数的完整性,但不保证完全保留在语言中。此外,Stdout和Stderr通过设计在Playground的一个流中合并。 如果您在fmt.Println(...)处使用fmt.Fprintln(os.Stdout, ... 或者明确定义{{1}} http://play.golang.org/p/PU3hxHCazA事物将按预期工作。

答案 2 :(得分:0)

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