我使用 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()。
来自 Golang 的 Denom() 源代码用于 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 表示的值被更改。那为什么不能在创建/修改部分完成呢?