首页 > 解决方案 > 循环指针

问题描述

请考虑片段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被覆盖了。我知道没有指针我不会有这个问题,正如 demo'd @ 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

解决方案


问题中的代码将局部变量的地址传递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)
}

推荐阅读