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

标签 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 文档指出:

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) 直到主函数返回后才会被调用。

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

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

最佳答案

您的打印目的地不一致。

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

关于Go - 延迟函数的不一致评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29316958/

相关文章:

c - 将 C 转换(翻译)为 Go 的工具?

c# - 通过使用带有 async/await 的 Linq 延迟执行而感到震惊

c# - 如何维护LINQ延迟执行?

firebase - 您如何在水平扩展的应用程序上管理 websockets?

GWT - 实现 TabLayoutPanel 的编程选项卡选择,然后滚动到选项卡中包含的特定元素?

javascript - jQuery 中的 .always() 或 .then() 回调先到什么?

javascript - 外部异步/延迟 javascript 是否保证在窗口 onload 事件之前执行?

xml - Go XML 解码节点 N 的属性 X

javascript - 如何通过其中包含 javascript 的 mongo_go_driver 运行聚合查询?

c - mmap 缓冲区到文件句柄