结构初始化不当?
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()
}
编码时遇到问题。当我在 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.
解决问题的最简单方法是在 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()
}