循环指针

str*_*ner 1 go

请考虑摘要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)
}
Run Code Online (Sandbox Code Playgroud)

我跑步的时候

Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Trying word8 0x40c140
Run Code Online (Sandbox Code Playgroud)

注意这里,我一直在打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)
}
Run Code Online (Sandbox Code Playgroud)

和,

$ 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)
}
Run Code Online (Sandbox Code Playgroud)

Cer*_*món 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])
}
Run Code Online (Sandbox Code Playgroud)

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

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)
}
Run Code Online (Sandbox Code Playgroud)