结构初始化不当?

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) {
    // ...
}

如果一个方法有一个指针接收者,调用该方法的值的地址(指针)将被传递给该方法。在方法内部,您将修改 pointed 值,而不是指针。指针指向调用方存在的相同值,因此修改了相同的值(而不是副本)。

仅这一更改就可以使您的代码正常工作。但是,您的程序的输出是不确定的,因为您从一个 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()访问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.Run 的开头更改 Inner.Value 一次。所以上面的代码做了很多不必要的 locks/unlocks 如果 Outer.Run() 中的循环会 等待 直到设置值,然后两个 goroutines 都可以删除读取变量而不锁定。一般来说,如果以后也可以更改变量,则在每个 read/write.

处都需要上述 locking/unlocking

解决问题的最简单方法是在 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 结构值发送到的输出通道:

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_