2016-09-17 1 views
1

Пожалуйста, проверьте этот фрагмент кода:различие между указателем и ломтиком значения в для диапазона цикла

package main 

import ( 
    "fmt" 
    "time" 
) 

type field struct { 
    name string 
} 

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

func main() { 
    fmt.Println("use values:") 

    // use values in range loop and go rountines 
    values := []field{{"one"},{"two"},{"three"}} 
    for _, v := range values { 
     go v.print() 
    } 

    time.Sleep(time.Second) 

    fmt.Println() 
    fmt.Println("use pointers:") 

    // use pointers in range loop and go rountines 
    poniters := []*field{{"one"},{"two"},{"three"}} 
    for _, v := range poniters { 
     go v.print() 
    } 

    time.Sleep(time.Second) 
} 

Ссылку здесь: https://play.golang.org/p/cdryPmyWt5

Код выше будет проверять различия между указателями и значениями в for loop, в то время как инструкция go также используется одновременно. Для кода:

values := []field{{"one"},{"two"},{"three"}} 
for _, v := range values { 
    go v.print() 
} 

мы знаем, что консоль будет печатать три три три как результат, потому что цикл работает в его конце, прежде чем приступить к выполнению goroutines, которые пишут V в качестве последнего элемента среза. Но как насчет указателей?

poniters := []*field{{"one"},{"two"},{"three"}} 
for _, v := range poniters { 
    go v.print() 
} 

кажется печатать один два три, почему?

Спасибо.

ответ

3

A: Параметры оцениваются до вызова функции. После того, как они вычисляются, параметры вызова передаются по значению к функции и вызываемая функция начинается выполнение, так что:

Первого go v.print() является синтаксическим сахаром для go (*field).print(&v) и
второго go v.print() является синтаксическим сахаром для go (*field).print(v).

Если первый for цикл заканчивается перед запуском горутов, &v - это то же самое для вызовов, и эти три вызова одинаковы. См. Код 2, добавив time.Sleep(100) после go v.print() в первый цикл. или с go func(v field) { v.print() }(v) по адресу The Go Playground (Code 3 with sync.WaitGroup).
Кроме того, у вас есть данные гонки здесь (см. B).

А для второго go (*field).print(v) здесь v является указателем и три goroutines параметра вычисляется перед вызовом print и имеют три разных адреса.

1- Попробуйте это на The Go Playground:

package main 

import (
    "fmt" 
    "time" 
) 

type field struct { 
    name string 
} 

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

func main() { 
    fmt.Println("use values:") 

    // use values in range loop and go rountines 
    values := []field{{"one"}, {"two"}, {"three"}} 
    for _, v := range values { 
     fmt.Println(&v) 
     go (*field).print(&v) //go v.print() 
    } 

    time.Sleep(time.Second) 

    fmt.Println() 
    fmt.Println("use pointers:") 

    // use pointers in range loop and go rountines 
    poniters := []*field{{"one"}, {"two"}, {"three"}} 
    for _, v := range poniters { 
     fmt.Println(v) 
     go (*field).print(v) //go v.print() 
    } 

    time.Sleep(time.Second) 
} 

выход:

use values: 
&{one} 
&{two} 
&{three} 
three 
three 
three 

use pointers: 
&{one} 
&{two} 
&{three} 
two 
one 
three 

2- Попробуйте это на The Go Playground:

package main 

import (
    "fmt" 
    "time" 
) 

type field struct { 
    name string 
} 

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

func main() { 
    fmt.Println("use values:") 

    // use values in range loop and go rountines 
    values := []field{{"one"}, {"two"}, {"three"}} 
    for _, v := range values { 
     fmt.Println(&v) 
     go v.print() //go (*field).print(&v) // 
     time.Sleep(100) 
    } 

    time.Sleep(time.Second) 

    fmt.Println() 
    fmt.Println("use pointers:") 

    // use pointers in range loop and go rountines 
    poniters := []*field{{"one"}, {"two"}, {"three"}} 
    for _, v := range poniters { 
     fmt.Println(v) 
     go v.print() //go (*field).print(v) // 
    } 

    time.Sleep(time.Second) 
} 

выход:

use values: 
&{one} 
one 
&{two} 
two 
&{three} 
three 

use pointers: 
&{one} 
&{two} 
&{three} 
one 
two 
three 

B: У вас есть гонки данных, попробуйте go build -raceyour code, а затем запустить созданный файл, WARNING: DATA RACE:

выход:

use values: 
================== 
WARNING: DATA RACE 
Read at 0x00c042030210 by goroutine 6: 
    runtime.convT2E() 
     Go/src/runtime/iface.go:155 +0x0 
    main.(*field).print() 
     .../m.go:14 +0x6c 

Previous write at 0x00c042030210 by main goroutine: 
    main.main() 
     .../m.go:22 +0x1c3 

Goroutine 6 (running) created at: 
    main.main() 
     .../m.go:23 +0x204 
================== 
two 
three 
three 

use pointers: 
one 
two 
three 
Found 1 data race(s) 
Смежные вопросы