进行不正确的结构初始化?

在编码时我遇到了一个问题。当我在 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}

也许是指针问题,但我不知道如何解决。


莫回无
浏览 107回答 2
2回答

慕侠2389804

您的代码中最明显的错误是它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()您访问该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()会等待,直到该值设置,事后都够程可以在不锁定读取变量。通常,如果变量也可以在以后更改,则在每次读/写时都需要上述锁定/解锁。

慕的地10843

解决问题的最简单方法是在Run函数中使用指针接收器:func (c *Inner) Run(value int) {&nbsp; &nbsp; out = make(chan int)&nbsp; &nbsp; c.Value = value&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(c.Value)&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second * 2)&nbsp; &nbsp; }}但另一种解决方案是使用一个输出通道,您可以将Inner结构值发送到该通道:func (c Inner) Run(value int) {&nbsp; &nbsp; out = make(chan int)&nbsp; &nbsp; c.Value = value&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(c.Value)&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second * 2)&nbsp; &nbsp; &nbsp; &nbsp; out <- c.Value&nbsp; &nbsp; }}然后在一个单独的 goroutine 中接收发送的值:for{&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; c.In.Run(42)&nbsp; &nbsp; &nbsp; &nbsp; <-out&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(out)&nbsp; &nbsp; }()&nbsp; &nbsp; time.Sleep(time.Second)&nbsp; &nbsp; &nbsp; &nbsp;}这是完整的代码:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "time")type Inner struct {&nbsp; &nbsp; Value int}var out chan intfunc (c Inner) Run(value int) {&nbsp; &nbsp; out = make(chan int)&nbsp; &nbsp; c.Value = value&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(c.Value)&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second * 2)&nbsp; &nbsp; &nbsp; &nbsp; out <- c.Value&nbsp; &nbsp; }}type Outer struct {&nbsp; &nbsp; In Inner}func (c Outer) Run()&nbsp; {&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; for{&nbsp; &nbsp; &nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c.In.Run(42)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <-out&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(out)&nbsp; &nbsp; &nbsp; &nbsp; }()&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; }}func main()&nbsp; {&nbsp; &nbsp; o := new(Outer)&nbsp; &nbsp; o.Run()}https://play.golang.org/p/Zt_NAsM98_
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go