【问题标题】:differences between pointer and value slice in for-range loopfor-range循环中指针和值切片的区别
【发布时间】:2017-01-25 14:05:08
【问题描述】:

请检查此代码 sn-p:

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循环中检查指针和值之间的差异,同时使用go语句。代码:

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

我们知道控制台会打印 三三三 作为结果,因为 for 循环在 goroutine 开始执行之前运行到它的末尾,它将 v 作为切片的最后一个元素。但是指针呢?

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

好像打印出一二三,为什么?

谢谢。

【问题讨论】:

    标签: pointers for-loop go goroutine


    【解决方案1】:

    A:在调用函数之前对参数进行评估。求值后,调用的参数按值传递给函数,被调用的函数开始执行,所以:

    第一个go v.print()go (*field).print(&v)
    的语法糖 第二个go v.print()go (*field).print(v) 的语法糖。

    如果第一个 for 循环在 goroutines 开始之前完成,&v 对于调用是相同的,这三个调用都是相同的。通过在go v.print() 之后添加time.Sleep(100) 到第一个循环来查看代码2。或在The Go Playground (Code 3 with sync.WaitGroup) 上使用go func(v field) { v.print() }(v)
    此外,您还有数据竞赛(参见 B)。

    对于第二个go (*field).print(v),这里的v 是指针,在调用print 之前评估了三个goroutines 参数,并且具有三个不同的地址

    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)
    

    【讨论】:

      猜你喜欢
      • 2018-12-20
      • 1970-01-01
      • 1970-01-01
      • 2022-12-17
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 2011-02-11
      • 1970-01-01
      相关资源
      最近更新 更多