Чтобы использовать goroutine для цикла for, почему работает итерация по указателям для структурирования, но не для структур - PullRequest
0 голосов
/ 12 декабря 2018

Фон

Я читаю 50 оттенков в Go, в частности Переменные итераций итераций в "for" утверждениях , и я собираюсь взять отрывок из этого.

Неверно

    package main

    import (  
        "fmt"
        "time"
    )

    type field struct {  
        name string
    }

    func (p *field) print() {  
        fmt.Println(p.name)
    }

    func main() {  
        data := []field{{"one"},{"two"},{"three"}}

        for _,v := range data {
            go v.print()
        }

        time.Sleep(3 * time.Second)
        //goroutines print: three, three, three
    }

Правильно

Измените []field{{"one"},{"two"},{"three"}} на []*field{{"one"},{"two"},{"three"}}, и one, two и three будут напечатаны в некотором порядке.

Процесс моей мысли

  1. В неправильном случае go v.print() заменяется на go (&v).print() компилятором, поскольку print() определено в приемнике указателя.

  2. До тех пор, пока не будет запущена порожденная программа, среда выполнения знает только, что она должна выполнить print(), но не имеет понятия, какой экземпляр должен быть передан в качестве получателя.

  3. Когда запускается порожденная программа, весьма вероятно, что цикл for завершился, поэтому, когда мы хотим решить, какое значение следует передать в качестве получателя, мы получаем адрес v, который используется совместно во времявесь цикл и обновляется в каждой итерации, поэтому мы передаем адресess последнего элемента data в качестве получателя print(), и поэтому мы получаем 3 three напечатанных.

Задача

Для меня,изменение []field на []*field только позволяет компилятору пропустить шаг 1, но не изменяет шаг 2 и шаг 3, поэтому я не знаю, почему это решает проблему.

Я полагаю, что должны быть некоторыенедостатки в моем мыслительном процессе, и я ценю любые советы.

Обновление

Мне довелось увидеть другую правильную реализацию здесь , и я думаю, что могу знать, где что-то пошло не такмой мыслительный процесс.

data := []field{{"one"}, {"two"}, {"three"}}

for i := range data {
    go data[i].print()
}

Дело в том, что указатель, который должен быть передан print() в качестве получателя, определяется на шаге 2 вместо шага 3. Это означает, что в неправильной версии мыпередача одного и того же адреса в каждой итерации, но содержимое, на которое он указывает (data), обновляется в каждой итерации.Однако в правильной версии указатели, передаваемые в print() в качестве получателя, указывают на фактические элементы field.То же относится и к случаю с использованием индексов.

1 Ответ

0 голосов
/ 12 декабря 2018

Ваш приемник - poniter, и вы должны определить срез поля как указатель, поэтому этот код работает

data := []*field{{"one"}, {"two"}, {"three"}}

, если вы измените свой приемник на не указатель, ваш код тоже работает.

func (p field) print() {
    fmt.Println(p.name)
}

data := []field{{"one"}, {"two"}, {"three"}}
...