遍历指针

问题描述 投票:1回答:2

请考虑代码段https://play.golang.org/p/GnhA1Tgw4sz,它是我遇到的问题的简化版本。我最初的问题是尝试将UDP消息发送到阵列中的目标,但我发现均匀分配存在问题。

还提供代码:

package main

import (
    "fmt"
    "time"
)

var (
    dests = [...]string{"word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8"}
)

func main() {
    fmt.Println("Hello!")
    fmt.Println("dests", dests)

    for _, dest := range dests {
        fmt.Println("dest is", dest)

        go func(dest_ptr *string) {
            fmt.Println("Trying", *dest_ptr, dest_ptr)
        }(&dest)

    }

    time.Sleep(200 * time.Second)
}

我跑步时,

Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140

[注意,我一直在按word8,这可能是因为&dest被覆盖了。我知道没有指针就不会有这个问题,如demodd @ https://play.golang.org/p/ZD9JIuvdypJ

我想使用指针,因为我最初的问题需要它。实现此目的的正确方法是什么?

来自C,这是一个很好的问题!据我了解,Go不会为每次迭代创建一个新变量,以后可能会解决此问题。

这是两个解决方法,

$ cat fixa.go
package main

import (
        "fmt"
        "time"
)

var (
        dests = [...]string{"word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8"}
)

func main() {
        fmt.Println("Hello!")
        fmt.Println("dests", dests)

        for i := range(dests){
                go func(dest_ptr * string){
                        fmt.Println("Handling", * dest_ptr, dest_ptr)
                }(&dests[i])

        }

        time.Sleep(200 * time.Second)
}

$ cat fixb.go
package main

import (
        "fmt"
        "time"
)

var (
        dests = [...]string{"word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8"}
)

func main() {
        fmt.Println("Hello!")
        fmt.Println("dests", dests)

        for _, dest := range dests {
                dest := dest //  create a new variable, using a declaration style that may seem odd but works fine in Go: 
                go func(dest_ptr *string) {
                        fmt.Println("Trying", *dest_ptr, dest_ptr)
                }(&dest)
        }

        time.Sleep(200 * time.Second)
}
go
2个回答
5
投票

问题中的代码将局部变量dest的地址传递给goroutine。在循环的每次迭代中都设置此局部变量。

通过将数组元素的地址传递给goroutine来解决:

for i := range dests {
    fmt.Println("dest is", dests[i])
    go func(dest_ptr *string) {
        fmt.Println("Trying", *dest_ptr, dest_ptr)
    }(&dests[i])
}

另一种方法是在循环的每次迭代中创建一个变量:

for _, dest := range dests {
    dest := dest  // <-- create new variable inside the loop
    fmt.Println("dest is", dest)
    go func(dest_ptr *string) {
        fmt.Println("Trying", *dest_ptr, dest_ptr)
    }(&dest)
}

3
投票

Go: Frequently Asked Questions (FAQ)

What happens with closures running as goroutines?

循环的每次迭代都使用变量v的相同实例,因此每个闭包都共享该单个变量。


对于您的示例,对于新变量,添加dest := dest语句,

package main

import (
    "fmt"
    "time"
)

var (
    dests = [...]string{"word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8"}
)

func main() {
    fmt.Println("dests", dests)

    for _, dest := range dests {
        dest := dest
        fmt.Println("dest is", dest)

        go func(dest_ptr *string) {
            fmt.Println("Trying", *dest_ptr, dest_ptr)
        }(&dest)

    }

    time.Sleep(200 * time.Second)
}

操场:https://play.golang.org/p/bDI3SB1PR7X

输出:

dests [word1 word2 word3 word4 word5 word6 word7 word8]
dest is word1
dest is word2
dest is word3
dest is word4
dest is word5
dest is word6
dest is word7
dest is word8
Trying word1 0x40c140
Trying word2 0x40c150
Trying word3 0x40c160
Trying word4 0x40c170
Trying word5 0x40c180
Trying word6 0x40c190
Trying word7 0x40c1a0
Trying word8 0x40c1b0
© www.soinside.com 2019 - 2024. All rights reserved.