dongtuoleng8624 2016-09-17 12:25
浏览 68
已采纳

范围循环中指针和值切片之间的差异

Please check this code snippet:

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)
}

Link here: https://play.golang.org/p/cdryPmyWt5

The code above is going to check the differences between pointers and values in a for loop, while go statement is also used at the same time. For code:

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

we know that the console will print three three three as result, because for loop runs into its end before goroutines start executing, which write v as the last element of the slice. But what about pointers?

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

It seems to print one two three, why?

Thanks.

  • 写回答

1条回答 默认 最新

  • dongxi3209 2016-09-17 12:45
    关注

    A: The parameters are evaluated before the function is called. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution, so:

    The first go v.print() is syntactic sugar for go (*field).print(&v) and
    the second go v.print() is syntactic sugar for go (*field).print(v).

    If first for loop finishes before goroutines start, &v is same for the calls and these three calls is all the same. See code 2 by adding time.Sleep(100) after go v.print() to first loop. or with go func(v field) { v.print() }(v) on The Go Playground (Code 3 with sync.WaitGroup).
    Also, you have data race here (see B).

    And for the second go (*field).print(v) here v is pointer and three goroutines parameter's evaluated before calling print and have three different addresses.

    1- Try this on 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)
    }
    

    output:

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

    2- Try this on 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)
    }
    

    output:

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

    B: You have data race, try go build -race your code, then run generated file, WARNING: DATA RACE:

    output:

    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)
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 yolov8边框坐标
  • ¥15 matlab中使用gurobi时报错
  • ¥15 WPF 大屏看板表格背景图片设置
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真