int在结构中的奇怪行为

时间:2016-03-05 12:57:39

标签: go atomic uint32

让我们说我们有这种结构(有史以来最简单的结构之一):

type some struct{
    I uint32
}

我们希望有一个这种类型的变量,并在for循环中原子递增(可能在另一个goroutine中,但现在故事是不同的)。我做了以下事情:

q := some{0}
for i := 0; i < 10; i++ {
        atomic.AddUint32(&q.I,1) // increment [1]
        fmt.Println(q.I)
}

我们得到了我们期望的东西,到目前为止一直很好,但如果我们按如下方式声明该类型的函数:

func (sm some) Add1(){
    atomic.AddUint32(&sm.I,1)
}

并在上面的示例(第[1]行)中调用此函数,值不会递增,我们只得到零。问题很明显 - 为什么?

这必须是基本的东西,但由于我是新手,我不会意识到这一点。

2 个答案:

答案 0 :(得分:7)

  

The Go Programming Language Specification

     

Calls

     

在函数调用中,将计算函数值和参数   通常的顺序。在评估它们之后,调用的参数   通过值传递给函数,并且被调用的函数开始   执行。函数的返回参数按值传递   当函数返回时返回调用函数。

接收方sm some按值传递给方法,从方法返回时将丢弃副本。使用指针接收器。

例如,

package main

import (
    "fmt"
    "sync/atomic"
)

type some struct {
    I uint32
}

func (sm *some) Add1() {
    atomic.AddUint32(&sm.I, 1)
}

func main() {
    var s some
    s.Add1()
    fmt.Println(s)
}

输出:

{1}
  

Go Frequently Asked Questions (FAQ)

     

When are function parameters passed by value?

     

与C系列中的所有语言一样,Go中的所有内容都通过   值。也就是说,一个函数总是得到一个东西的副本   传递,好像有一个赋值的赋值语句   到参数。例如,将int值传递给函数   制作int的副本,并传递指针值制作副本   指针,但不指向它指向的数据。

     

Should I define methods on values or pointers?

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value
     

对于不习惯指针的程序员来说,区别   这两个例子可能令人困惑,但事实上情况确实如此   非常简单。在类型上定义方法时,接收器(在   上面的例子)就像它是一个参数一样   方法。是将接收器定义为值还是指针   同样的问题,那么,函数参数是否应该是a   值或指针。有几点需要考虑。

     

首先,最重要的是,该方法是否需要修改   接收器?如果是,接收器必须是指针。 (切片和地图   作为参考,所以他们的故事更微妙,但是   在接收器必须的方法中更改切片长度的实例   仍然是一个指针。)在上面的例子中,如果pointerMethod修改   在s的字段中,调用者将看到这些更改,但valueMethod是   用调用者的参数的副本调用(这是定义的   传递一个值,所以改变它使得调用者看不见。

     

顺便说一句,指针接收器与Java中的情况相同,   虽然在Java中,指针隐藏在封面下;这是Go的   价值接收器是不寻常的。

     

其次是效率的考虑。如果接收器很大,a   例如,大结构,使用指针要便宜得多   接收机。

     

接下来是一致性。如果该类型的某些方法必须具有   指针接收器,其余的也应该是,所以方法集是   无论使用何种类型,都是一致的。请参阅有关的部分   方法设置细节。

     

对于基本类型,切片和小结构等类型,值   接收器非常便宜,所以除非方法的语义要求   一个指针,一个值接收器是高效和清晰的。

答案 1 :(得分:2)

你的函数需要接收一个指针来指示要递增的值,这样你就不会传递结构的副本,而在下一次迭代中我可以递增。

package main

import (
"sync/atomic"
"fmt"
)

type some struct{
    I uint32
}

func main() {
q := &some{0}
for i := 0; i < 10; i++ {
        q.Add1()
        fmt.Println(q.I)
}
}

func (sm *some) Add1(){
    atomic.AddUint32(&sm.I,1)
}