2016-03-05 1 views
4

Допустим, у нас есть этот вид структуры (один из самых простых когда-либо):Странное поведение междунар внутри структуры

type some struct{ 
    I uint32 
} 

И мы хотим иметь переменную этого типа и атомарно приращение в течение (возможно, в другой горутин, но теперь история отличается). Я делаю следующее:

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]) значение не увеличивается и мы получаем только нули. Вопрос очевиден - почему?

Это должно быть что-то основное, но поскольку я новичок, я не понимаю.

+0

Подход к указателям языка Go немного запутан. В Go вы определяете приемник метода, чтобы указать, к какой структуре присоединяется определенная функция, чтобы он мог быть вызван как метод. –

ответ

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 передается б y значение. То есть, функция всегда получает копию вещи, которая прошла , как если бы был оператор присваивания, присваивающий этому параметру значение . Например, передача значения int функции делает копию int, и передача значения указателя делает копию указателем, но не данными, на которые указывает.

Should I define methods on values or pointers?

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

Для программистов непривычных к указателям, различие между эти два примера могут сбивать с толку, но ситуация на самом деле очень просто. При определении метода для типа приемник (s в примерах ) ведет себя точно так же, как если бы это был аргумент метода .Независимо от того, определять ли получатель в качестве значения или как указатель тот же вопрос, то как должен быть аргумент функции значением или указателем. Существует несколько соображений.

Во-первых, и, самое главное, нужен ли метод для изменения приемника ? Если это так, приемник должен быть указателем. (Ломтики и карты действуют как ссылки, поэтому их рассказ немного более тонкий, но для экземпляр, чтобы изменить длину среза в методе, получатель должен по-прежнему быть указателем.) В приведенных выше примерах, если pointerMethod изменяет поля s, вызывающий будет видеть эти изменения, но valueMethod - , вызываемый с копией аргумента вызывающего абонента (это определение , передающее значение), поэтому изменения, которые он делает, будут невидимы для вызывающего.

Кстати, приемники-указатели идентичны ситуации в Java, , хотя в Java указатели скрыты под обложками; это Go's Ценностные приемники, которые являются необычными.

Во-вторых, рассмотрение эффективности. Если приемник большой, например, большой структуры, будет гораздо дешевле использовать указатель .

Следующая последовательность. Если некоторые из методов этого типа должны иметь указатели-указатели, то остальное тоже должно быть, поэтому набор методов равен , независимо от того, как используется этот тип. Подробные сведения см. В разделе наборы меток .

Для таких типов, как основные типов, ломтики и малые структуры, значение приемника очень дешево, поэтому, если семантика методы требует указателя, значение приемник является эффективным и ясно.

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) 
}