在范围循环中指针和值切片之间的差异 [英] differences between pointer and value slice in for-range loop

查看:291
本文介绍了在范围循环中指针和值切片之间的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请检查以下代码段:

 包主

导入(
fmt
time


类型字段struct {
名称字符串
}

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

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

//在范围循环中使用值并执行rountines
values:= [] field {{one},{two},{three}}
for _,v:= range values {
go v.print()
}

time.Sleep(time.Second)

fmt。 println()
fmt.Println(use pointers:)

//在范围循环中使用指针并执行rountines
poniters:= [] * field {{one },{two},{three}}
for _,v:= range poniters {
go v.print()
}

time.Sleep(time.Second)
}

链接: https://play.golang.org/p/cd ryPmyWt5



上面的代码将检查for循环中指针和值之间的差异,而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()
}

它似乎打印一二三,为什么?



谢谢。


<解决方案

A:在调用函数之前评估参数。在它们被评估之后,调用的参数被传递给函数,被调用的函数开始执行,所以:

第一个 go v.print() go(* field).print(& v)

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

如果在goroutines开始之前循环的第一个结束,& v 对于通话来说是相同的,这三个通话都是一样的。通过在第一个循环中添加 time.Sleep(100)来查看代码2,将 go v.print()或在 go func(v field){v.print()}(v) go =nofollow> Go Playground(代码3与 sync.WaitGroup

此外,您还有数据竞赛在这里(见B)。

对于第二个 go(* field).print(v)这里 v在调用 print 有三个不同的地址之前,是指针和三个goroutines参数。 b

1-在 Go Playground 上试试这个:

 包裹主要

进口(
fmt
时间


类型字段struct {
名称字符串
}

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

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

//使用值范围循环和去轮胎
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:)

//在范围循环中使用指针并执行rountines
poniters:= [] * field {{one},{two},{three}}
为_,v:=范围poniters {
fmt.Println(v)
go(* field).print(v)// go v.print()
}

time.Sleep(time.Second)
}

输出:

 使用值:
& {one}
& {two}
& amp ; {三}




使用指针:
& {one}
& {two}
& {three}
two
one
three






2-在 Go Playground 上试试这个:

  package main 

import(
fmt
time


类型字段struct {
名称字符串
}

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

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

//使用范围循环中的值并执行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:)

//在范围循环中使用指针并执行rountines
poniters:= [] * field {{one},{two},{ (三)}}
for _,v:= range poniters {
fmt.Println(v)
go v.print()// go(* field).print(v) //
}

time.Sleep(time.Second)
}

输出:

 使用值:
& {one}
one
& {two}
two
& {three}
three

使用指针:
& {one}
& {two}
& {three}
one
two
three






B:你有数据竞争,试试 go build -race 您的代码,然后运行生成的文件 WARNING:DATA RACE



输出:

 使用值:
==================
警告:DATA RACE
通过goroutine 6读取0x00c042030210:
runtime.convT2E()
Go / src / runtime / iface.go:155 + 0x0
main。(* field ).print()
... / m.go:14 + 0x6c

先前在主goroutine处写入0x00c042030210:
main.main()
。 ../m.go:22 + 0x1c3

创建的Goroutine 6(running):
main.main()
... / m.go:23 + 0x204
==================
two
three
three

使用指针:
one
two
three
找到1个数据竞赛


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.

解决方案

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)

这篇关于在范围循环中指针和值切片之间的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆