簡體   English   中英

去不正確的struct初始化?

[英]Go improper struct initialization?

雖然編碼我遇到了問題。 當我在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}

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

代碼中最明顯的錯誤是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.Value字段時,在Inner.Run()鎖定中:

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.Run的開頭更改Inner.Value一次。 所以上面的代碼執行了很多不必要的鎖定/解鎖,如果Outer.Run()的循環等到設置值,則可以刪除這些鎖定/解鎖,之后兩個goroutine都可以讀取變量而不鎖定。 通常,如果變量也可以在以后更改,則在每次讀/寫時都需要上述鎖定/解鎖。

解決問題的最簡單方法是在Run函數中使用指針接收器:

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

但另一種解決方案是使用out通道,您可以向其發送Inner struct值:

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

然后在一個單獨的goroutine中接收發送的值:

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_

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM