精度を上げるために、math/big.Rat を使用して数値を表現しました。Denom() は数値の分母を返し、Cmp() は 2 つの数値を比較します。どちらも純粋な読み取り専用関数のようです。しかし、データ競合を有効にしてコードを実行したところ、私の仮定全体が間違っていました。これらの関数が同じ 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() を同時に使用しないでください。
ref の Golangからの Denom() ソース。
// 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 によって表される値が変更されない限り、同じ値の繰り返し計算 (同時安全ではない) です。では、なぜこれを作成/変更部分で行うことができないのでしょうか?