pointers - 结构初始化不当?

标签 pointers go struct concurrency goroutine

在编码时我遇到了一个问题。当我在 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.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() 中的循环将等待直到设置值,然后两者都可以删除goroutines 可以在不锁定的情况下读取变量。一般来说,如果以后也可以更改变量,则每次读/写时都需要进行上述锁定/解锁。

关于pointers - 结构初始化不当?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37043713/

相关文章:

c++ - 在构造函数中初始化数组

types - Go 中的别名类型仅在未命名时才可分配?

go - 第一个完成时如何安全地绕过其他 goroutine 的结果

c - 语义版本控制 : minor or major change?(第二部分)

c - C 上的初始化结构问题

c - 在指针上使用成员访问运算符

c - 链表的并集

c++ - static_cast是否正在创建新的子对象?

docker - 在构建 docker 容器时使用本地依赖项

c - C 中带有结构体指针的链表节点