简体   繁体   English

去不正确的struct初始化?

[英]Go improper struct initialization?

While coding I encountered a problem. 虽然编码我遇到了问题。 When I use method of inner struct in goroutine, I can't see inner state like in this code. 当我在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()
}

Program printing: 程序打印:

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

Maybe it's pointer problem, but I don't know how resolve it. 也许这是指针问题,但我不知道如何解决它。

The most obvious error in your code is that Inner.Run() has a value-receiver, which means it gets a copy of the Inner type. 代码中最明显的错误是Inner.Run()有一个值接收器,这意味着它获得了Inner类型的副本。 When you modify this, you modify the copy, and the caller won't see any change on the Inner value. 修改此项后,您将修改副本,并且调用者将看不到Inner值的任何更改。

So first modify it to have a pointer-receiver: 所以首先修改它以使用指针接收器:

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

If a method has a pointer-receiver, the address (pointer) of the value the method is called on will be passed to the method. 如果方法具有指针接收器,则调用该方法的值的地址(指针)将传递给该方法。 And inside the method you will modify the pointed value, not the pointer. 在方法内部,您将修改指向的值,而不是指针。 The pointer points to the same value that is present at the caller, so the same value is modified (and not a copy). 指针指向调用者处出现的相同值,因此修改了相同的值(而不是副本)。

This change alone may make your code work. 仅此更改可能会使您的代码工作。 However, the output of your program is non-deterministic because you modify a variable (field) from one goroutine, and you read this variable from another goroutine too, so you must synchronize access to this field in some way. 但是,程序的输出是非确定性的,因为您从一个goroutine修改变量(字段),并且您也从另一个goroutine中读取此变量,因此您必须以某种方式同步对此字段的访问。

One way to synchronize access is using sync.RWMutex : 同步访问的一种方法是使用sync.RWMutex

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

When you create your Outer value, initialize this mutex: 创建Outer值时,请初始化此互斥锁:

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

Or in one line: 或者在一行中:

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

And in Inner.Run() lock when you access the Inner.Value field: 当您访问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)
    }
}

And you also have to use the lock when you access the field in Outer.Run() : 当您访问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()
    }
}

Note: 注意:

Your example only changes Inner.Value once, in the beginning of Inner.Run . 您的示例仅在Inner.Run的开头更改Inner.Value一次。 So the above code does a lot of unnecessary locks/unlocks which could be removed if the loop in Outer.Run() would wait until the value is set, and afterwards both goroutines could read the variable without locking. 所以上面的代码执行了很多不必要的锁定/解锁,如果Outer.Run()的循环等到设置值,则可以删除这些锁定/解锁,之后两个goroutine都可以读取变量而不锁定。 In general if the variable can be changed at later times too, the above presented locking/unlocking is required at each read/write. 通常,如果变量也可以在以后更改,则在每次读/写时都需要上述锁定/解锁。

The simplest way to resolve your issue is to use a pointer receiver in your Run function: 解决问题的最简单方法是在Run函数中使用指针接收器:

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

But another solution would be to use an out channel to which you can send the Inner struct value: 但另一种解决方案是使用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
    }
}

Then in a separate goroutine to receive back the sent value: 然后在一个单独的goroutine中接收发送的值:

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

Here is the full code: 这是完整的代码:

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_ https://play.golang.org/p/Zt_NAsM98_

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM