gpt4 book ai didi

go - big.Rat 中的数据竞赛

转载 作者:数据小太阳 更新时间:2023-10-29 03:14:35 28 4
gpt4 key购买 nike

为了准确性,我使用 math/big.Rat 来表示数字。Denom() 返回数字的分母,Cmp() 用于比较两个数字。它们似乎都是纯只读函数。但是当我在启用数据竞争的情况下运行我的代码时,我的整个假设都出错了。当这些函数与同一个 Rat 实例同时调用时,系统会抛出数据竞争场景。这些功能不是只读的吗?

我的测试用例

package main

import (
"math/big"
"sync"
)

func main() {
x := big.NewRat(5, 1)
wg := new(sync.WaitGroup)

// just for testing
for i := 0; i < 10; i++ {
go func() {
wg.Add(1)
defer wg.Done()
if i%2 == 0 {
x.Cmp(x)
} else {
x.Denom()
}
}()
}
wg.Wait()
}

当我检查源代码时,每次调用 Denom() 函数时,它都会重置同一个对象中的值。这是源代码中的问题吗?或者我不应该同时使用 Rat Denom() 和 Cmp()。

Denom() 来源 Golang for ref .

// Denom returns the denominator of x; it is always > 0.
400 // The result is a reference to x's denominator; it
401 // may change if a new value is assigned to x, and vice versa.
402 func (x *Rat) Denom() *Int {
403 x.b.neg = false // the result is always >= 0
404 if len(x.b.abs) == 0 {
405 x.b.abs = x.b.abs.set(natOne) // materialize denominator
406 }
407 return &x.b
408 }

我根据下面的讨论添加了更多要点,我承认我在将变量“i”用于预期目的时犯了一个错误(但它仍然可以显示数据竞争场景)。

我的观点是在 Denom() 中执行的操作不会修改 Rat 表示的值。这可以在创建 Rat 以表示值或在 Rat 中设置新值时执行。我担心的是重复计算(不是并发安全)相同的值一次又一次,除非 Rat 表示的值发生变化。那为什么不能在创建/修改部分完成呢?

最佳答案

你有一个明确的竞争条件,简而言之,竞争条件是当两个以上的异步例程(线程、进程、协同例程等)试图访问(写入或读取)资源(内存或 i/o 设备)并且这些例程中至少有一个有写入意图。

在您的情况下,您正在创建访问共享资源 (var x Rat) 的 goroutines,您可能会注意到方法 Denom() 修改了它自己的值在 403405 行中,似乎 Cmp() 方法只是读取。我们所要做的就是用 RWMutex 保护内存:

package main 

import (
"math/big"
"sync"
)

func main() {
x := big.NewRat(5, 1)
wg := new(sync.WaitGroup)
mutex := new(sync.RWMutex)

wg.Add(10) // all goroutines

for i := 0; i < 10; i++ {

go func(index int) {
defer wg.Done()

if index%2 == 0 {

mutex.RLock() // locks only for reading
x.Cmp(x)
mutex.RUnlock() // unlocks for reading

} else {

mutex.Lock() // locks for writing
x.Denom()
mutex.Unlock() // unlock for writing

}
}(i)
}

wg.Wait()
}

请注意,我们使用 RLock 和 RUnlock 进行读操作,使用 Lock 和 Unlock() 进行写操作。另外,如果你知道要创建的 goroutine 的数量,我总是建议在一行中执行 wg.Add(n) 因为如果你在 wg.Add(1) 之后执行 go func(){ ...} 你会有麻烦的。

事实上,在 goroutine 中使用 for 索引确实是一个常见的错误,总是将它们作为参数传递。

最后,我建议您使用 -race 标志来go rungo build 命令,例如:

go run -race rat.go

事实上,您将看到您的代码与我的解决方案之间的区别,只需使用 -race

关于go - big.Rat 中的数据竞赛,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40244358/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com