[英]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()
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.