繁体   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