【问题标题】:Any concurrency issues when multiple goroutines access different fields of the same struct多个 goroutine 访问同一结构的不同字段时的任何并发问题
【发布时间】:2021-10-16 10:33:57
【问题描述】:

如果我们在不同的 go co-routines 中访问结构的互斥字段,是否存在任何并发问题?

我记得在某处读到过,如果两个并行线程访问同一个对象,它们可能会在 cpu 的不同内核上运行,这两个内核都具有不同的 cpu 级缓存和所讨论对象的不同副本。 (与 Go 无关)

以下代码是否足以正确实现功能或是否需要使用额外的同步机制?

package main

import (
    "fmt"
    "sync"
)

type structure struct {
    x string
    y string
}

func main() {
    val := structure{}
    wg := new(sync.WaitGroup)
    wg.Add(2)
    go func1(&val, wg)
    go func2(&val, wg)
    wg.Wait()
    fmt.Println(val)
}

func func1(val *structure, wg *sync.WaitGroup) {
    val.x = "Test 1"
    wg.Done()
}

func func2(val *structure, wg *sync.WaitGroup) {
    val.y = "Test 2"
    wg.Done()
}

编辑:-对于那些问为什么不频道的人,不幸的是这不是我正在处理的实际代码。 func 都调用了不同的 api 并在结构中获取数据,这些结构中有一个 pragma.DoNotCopy 询问 protobuf 自动生成器为什么他们认为这是一个好主意。所以这些数据不能通过通道发送,否则我必须创建另一个结构来发送数据或要求 linter 停止抱怨。或者我可以发送一个指向对象的指针,但感觉它也在共享内存。

【问题讨论】:

  • 澄清一下,CPU 没有任何对象或结构的概念,这些边界仅存在于您使用的语言中。
  • memory model 描述了需要同步的位置。每个字段都是内存模型中的一个单独变量。
  • 为什么不通过渠道使用并发?您能否详细介绍一下问题的背景?
  • 这是我正在考虑的具体场景:- main func 创建字段,也许是 cpu1 的 L1 缓存,缓存数据,现在 func1& func2 在 cpu2 上运行,将数据写入这些字段并完成协程.当 main 再次在 cpu1 上运行时,它不会仍然认为它的缓存是正确的数据。基本上是这样的tutorialspoint.com/parallel_computer_architecture/… 或者协程保证在同一个cpu上运行?

标签: go


【解决方案1】:

当对共享资源的至少一个访问是写入时,您必须进行同步。

您的代码正在执行写访问,是的,但是不同的结构字段具有不同的内存位置。所以你没有访问 shared 变量。

如果您使用比赛检测器运行程序,例如。 go run -race main.go不会打印警告。

现在在func1 中添加fmt.Println(val.y) 并再次运行,它将打印:

WARNING: DATA RACE
Write at 0x00c0000c0010 by goroutine 8:
... rest of race warning

【讨论】:

  • 这是我正在考虑的具体场景:- main func 创建字段,也许是 cpu1 的 L1 缓存,缓存数据,现在 func1& func2 在 cpu2 上运行,将数据写入这些字段并完成协程.当 main 再次在 cpu1 上运行时,它不会仍然认为它的缓存是正确的数据。我猜基本上需要像tutorialspoint.com/parallel_computer_architecture/… 这样的东西来修复它
【解决方案2】:

Go 中首选的方式是通信内存而不是共享内存。

实际上,这意味着您应该使用我在这篇博文中展示的 Go 频道。

https://marcofranssen.nl/concurrency-in-go

如果您真的想坚持共享内存,则必须使用 Mutex。

https://tour.golang.org/concurrency/9

但是,这会导致上下文切换和 Go 例程同步,从而减慢您的程序。

使用渠道的示例

package main

import (
    "fmt"
    "time"
)

type structure struct {
    x string
    y string
}

func main() {
    val := structure{}
    c := make(chan structure)
    go func1(c)
    go func2(c)

    func(c chan structure) {
        for {
            select {
            case v, ok := <-c:
                if !ok {
                    return
                }

                if v.x != "" {
                    fmt.Printf("Received %v\n", v)
                    val.x = v.x
                }
                if v.y != "" {
                    fmt.Printf("Received %v\n", v)
                    val.y = v.y
                }
                if val.x != "" && val.y != "" {
                    close(c)
                }
            }
        }
    }(c)
    fmt.Printf("%v\n", val)
}

func func1(c chan<- structure) {
    time.Sleep(1 * time.Second)
    c <- structure{x: "Test 1"}
}

func func2(c chan<- structure) {
    c <- structure{y: "Test 2"}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-17
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-24
    • 1970-01-01
    相关资源
    最近更新 更多