遍历切片并进行goroutine方法调用,背后的逻辑是什么?

huangapple go评论73阅读模式
英文:

go for range slice and goroutine method invocation,the logic behind

问题

代码如下:

package main

import (
	"fmt"
	"time"
)

type field struct {
	name string
}

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

func main() {
	data := []field{{"one"}, {"two"}, {"three"}}
	for _, v := range data {
		go v.print()
	}
	time.Sleep(3 * time.Second)
}

我知道这段代码是错误的,因为在 for-range 循环中重用了 for 循环变量。

当 goroutine 有机会启动时,v 的值可能已经被修改。所以打印的结果将是 "three,three,three"

但是当我们将 data 变量修改为另一种声明方式时:

data := []*field{{"one"}, {"two"}, {"three"}}

打印的结果将是 "one, two, three"

我不明白其中的原因。指针是否有任何区别,或者是否存在其他不同的机制?

我从这篇文章中读到了这个问题。但是作者没有解释原因。或者这只是一个输出正确的偶然事件。


<details>
<summary>英文:</summary>

The code is like the following:

    package main
        
        import (
        	&quot;fmt&quot;
        	&quot;time&quot;
        )
        
        type field struct {
        	name string
        }
        
        func (p *field) print() {
            fmt.Println(p.name)
        }
        
        func main() {
        	data := []field{{&quot;one&quot;},{&quot;two&quot;},{&quot;three&quot;}}
        	for _,v := range data {
        		go v.print()
        	}
        	time.Sleep(3 * time.Second)
        }

I know that the code is wrong,because the for loop variable is reused in the for-range loop.

When the goroutine has got the chance to launch,the value of `v` might has been modified. so the print result will be `&quot;three,three,three&quot;`.

But when we modify the data variable into another declaration as:

    data := []*field{{&quot;one&quot;},{&quot;two&quot;},{&quot;three&quot;}}

the print result will be `&quot;one ,two,three&quot;`.

I didn&#39;t get the point of why. Does the pointer make any difference or any different mechanism is on this?

I read this from [this article][1]. But the poster didn&#39;t not tell why. Or it&#39;s just a incident the output is right.


  [1]: http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#range_val_update

</details>


# 答案1
**得分**: 1

在第一个循环中,`v` 是 `field` 项的 *值*。因为 `v` 是可寻址的,它会自动作为指针接收器被引用到 `print()` 方法中。所以 `v.print()` 使用的是 `v` 本身的地址,而该地址的内容在每次循环迭代时被覆盖。

当你将声明更改为使用 `*field` 时,`v` 现在是指向 `field` 值的指针。在这种情况下调用 `v.print()`,你操作的是 `v` 指向的值,该值存储在 `data` 中,而对 `v` 的覆盖没有影响。

<details>
<summary>英文:</summary>

In the first loop, `v` is the *value* of a `field` item. Because `v` is addressable, it is automatically referenced as the pointer receiver for the `print()` method. So `v.print()` is using the address of `v` itself, and the contents of that address is overwritten each iteration of the loop. 

When you change the declaration to use a `*field`, `v` is now a pointer to a `field` value. When you call `v.print()` in this case, you are operating on the value that `v` points to, which is stored in `data`, and the overwriting of `v` has no effect. 

</details>



huangapple
  • 本文由 发表于 2015年6月1日 23:19:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/30577212.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定