2016-05-05 85 views
3

編碼時遇到了問題。當我在goroutine中使用內部結構的方法時,我看不到像這個代碼中的內部狀態。去不正確的結構初始化?

package main 

import (
    "fmt" 
    "time" 
) 

type Inner struct { 
    Value int 
} 

func (c Inner) Run(value int) { 
    c.Value = value 
    for { 
     fmt.Println(c.Value) 
     time.Sleep(time.Second * 2) 
    } 

} 

type Outer struct { 
    In Inner 
} 

func (c Outer) Run() { 
    go c.In.Run(42) 

    for { 
     time.Sleep(time.Second) 
     fmt.Println(c.In) 
    } 
} 

func main() { 
    o := new(Outer) 
    o.Run() 
} 

程序打印:

from inner: {42} 
from outer: {0} 
from outer: {0} 
from inner: {42} 
from outer: {0} 
from inner: {42} 
from outer: {0} 
from outer: {0} 

也許是指針的問題,但我不知道如何解決它。

回答

3

代碼中最明顯的錯誤是Inner.Run()有一個值接收器,這意味着它獲得Inner類型的副本。當您修改此項時,您將修改該副本,並且調用者在Inner值上看不到任何更改。

所以首先對其進行修改以有一個指針接收器:

func (c *Inner) Run(value int) { 
    // ... 
} 

如果一個方法調用該方法上的值的指針接收器,該地址(指針)將被傳遞給該方法。而在這個方法裏面你會修改指向的的值,而不是指針。指針指向與調用者相同的值,因此相同的值被修改(而不是副本)。

僅此更改可能會使您的代碼正常工作。但是,程序的輸出是非確定性的,因爲您從一個goroutine修改了一個變量(字段),並且您從另一個goroutine中讀取了該變量,因此您必須以某種方式同步對該字段的訪問。

一個同步接入方式是使用sync.RWMutex

type Inner struct { 
    m  *sync.RWMutex 
    Value int 
} 

當您創建Outer值,初始化這個互斥:

o := new(Outer) 
o.In.m = &sync.RWMutex{} 

或者在同一行:

o := &Outer{In: Inner{m: &sync.RWMutex{}}} 

並在Inner.Run()鎖定時訪問th ËInner.Value場:

func (c *Inner) Run(value int) { 
    c.m.Lock() 
    c.Value = value 
    c.m.Unlock() 

    for { 
     c.m.RLock() 
     fmt.Println(c.Value) 
     c.m.RUnlock() 
     time.Sleep(time.Second * 2) 
    } 
} 

,你也有當您訪問Outer.Run()現場使用的鎖:

func (c Outer) Run() { 
    go c.In.Run(42) 

    for { 
     time.Sleep(time.Second) 
     c.In.m.RLock() 
     fmt.Println(c.In) 
     c.In.m.RUnlock() 
    } 
} 

注:

你舉的例子只是改變Inner.Value一次, Inner.Run的開頭。因此,上面的代碼會執行大量不必要的鎖定/解鎖,如果Outer.Run()中的循環將等待,直到設置該值,然後這兩個goroutine都可以在不鎖定的情況下讀取該變量,則可以將其刪除。一般而言,如果變量也可以在稍後改變,則在每次讀取/寫入時需要上述鎖定/解鎖。

+0

感謝您的決心!現在我明白它是如何工作的。現在代碼工作很好。 –

3

解決您的問題,最簡單的方法是在你的Run功能使用指針接收器:

func (c *Inner) Run(value int) { 
    out = make(chan int) 
    c.Value = value 
    for { 
     fmt.Println(c.Value) 
     time.Sleep(time.Second * 2) 
    } 
} 

但另一種解決方案是使用了通道,您可以發送Inner結構值:

在一個單獨的goroutine以接收回送值
func (c Inner) Run(value int) { 
    out = make(chan int) 
    c.Value = value 
    for { 
     fmt.Println(c.Value) 
     time.Sleep(time.Second * 2) 
     out <- c.Value 
    } 
} 

然後:

for{ 
    go func() { 
     c.In.Run(42) 
     <-out 
     fmt.Println(out) 
    }() 
    time.Sleep(time.Second)  
} 

下面是完整的代碼:

package main 

import (
    "fmt" 
    "time" 
) 

type Inner struct { 
    Value int 
} 

var out chan int 

func (c Inner) Run(value int) { 
    out = make(chan int) 
    c.Value = value 
    for { 
     fmt.Println(c.Value) 
     time.Sleep(time.Second * 2) 
     out <- c.Value 
    } 
} 

type Outer struct { 
    In Inner 
} 

func (c Outer) Run() {  

    for{ 
     go func() { 
      c.In.Run(42) 
      <-out 
      fmt.Println(out) 
     }() 
     time.Sleep(time.Second)  
    } 
} 

func main() { 
    o := new(Outer) 
    o.Run() 
} 

https://play.golang.org/p/Zt_NAsM98_