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

报告相同问题?

悬赏问题

  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容