Цикл по указателям - PullRequest
       0

Цикл по указателям

1 голос
/ 08 октября 2019

Пожалуйста, рассмотрите фрагмент 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)
}

Ответы [ 2 ]

5 голосов
/ 08 октября 2019

Код в вопросе передает адрес локальной переменной dest в подпрограммы. Эта локальная переменная устанавливается на каждой итерации цикла.

Исправление путем передачи адреса элементов массива в функции:

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 голосов
/ 08 октября 2019

Go: часто задаваемые вопросы (ЧАВО)

Что происходит с замыканиями, выполняющимися как процедуры?

каждая итерацияЦикл использует один и тот же экземпляр переменной 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...