Goレース検出器は、GoogleのC / C ++サニタイザーテクノロジーに基づいて実装されています。コンパイラはすべてのメモリアクセスを検出し、これらのメモリアドレスのアクセス(読み取りまたは書き込み)を監視するコードを追加します。コードの実行中、レース検出器は共有変数への非同期アクセスを監視できます。レースが表示されると、警告メッセージが出力されます。このテクノロジーは、Google内で大いに役立ち、Chromiumやその他のコードで多数の同時実行の問題を検出しました。このテクノロジーはGo1.1で導入され、標準ライブラリの42の並行性の問題がすぐに発見されました。現在、レース検出器はGoの継続的インテグレーションプロセスの一部になっています。このツールの使い方を見てみましょう。
Goコードをコンパイル、テスト、または実行するときに、raceパラメーターを追加することで並行性の問題が見つかる場合があります。たとえば、上記の例では、raceパラメーターを指定して実行し、並行性の問題があるかどうかを確認できます。-race counter.goを実行すると、警告メッセージが出力されます。
示例问题代码:
import (
"fmt"
"sync"
)
func main() {
var count = 0
// 使用WaitGroup等待10个goroutine完成
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// 对变量count执行10次加1
for j := 0; j < 100000; j++ {
count++
}
}()
}
// 等待10个goroutine完成
wg.Wait()
fmt.Println(count)
}
結果は次のとおりです。
この警告は、並行性の問題があることを通知するだけでなく、どのゴルーチンがどの行のどの変数に書き込み操作を行っているか、どのゴルーチンがどの行のどの変数に読み取り操作を行っているかを示します。読み取りと書き込みの同時実行訪問により、データの競合が発生しました。この例では、ゴルーチン10にはメモリアドレス0x00c000126010(counter.goファイルの16行目)への読み取り操作があり、ゴルーチン7にはメモリアドレス0x00c000126010(counter.goファイルの16行目)への書き込み操作があります。また、複数のゴルーチンが同時に読み取りと書き込みを行う可能性があるため、警告メッセージが非常に長くなる可能性があります。このツールは非常に便利ですが、実装されているため、実際のアドレスの読み取りと書き込み時にのみ検出できるため、コンパイル中にデータの競合の問題を検出することはできません。さらに、実行中は、データ競合がトリガーされた後にのみ検出できます。トリガーされなかった場合(たとえば、データ競合の問題は、2月14日のゼロ点または11月11日のゼロ点でのみ発生します) 、検出されません。から。さらに、オンラインでレースを使用してプログラムを展開すると、パフォーマンスに影響します。go tool compile -race -S counter.goを実行して、count ++の前後のコンパイル済みコードに焦点を当てて、カウンター例のコードを表示します。
0x002a 00042 (counter.go:13) CALL runtime.racefuncenter(SB)
......
0x0061 00097 (counter.go:14) JMP 173
0x0063 00099 (counter.go:15) MOVQ AX, "".j+8(SP)
0x0068 00104 (counter.go:16) PCDATA $0, $1
0x0068 00104 (counter.go:16) MOVQ "".&count+128(SP), AX
0x0070 00112 (counter.go:16) PCDATA $0, $0
0x0070 00112 (counter.go:16) MOVQ AX, (SP)
0x0074 00116 (counter.go:16) CALL runtime.raceread(SB)
0x0079 00121 (counter.go:16) PCDATA $0, $1
0x0079 00121 (counter.go:16) MOVQ "".&count+128(SP), AX
0x0081 00129 (counter.go:16) MOVQ (AX), CX
0x0084 00132 (counter.go:16) MOVQ CX, ""..autotmp_8+16(SP)
0x0089 00137 (counter.go:16) PCDATA $0, $0
0x0089 00137 (counter.go:16) MOVQ AX, (SP)
0x008d 00141 (counter.go:16) CALL runtime.racewrite(SB)
0x0092 00146 (counter.go:16) MOVQ ""..autotmp_8+16(SP), AX
......
0x00b6 00182 (counter.go:18) CALL runtime.deferreturn(SB)
0x00bb 00187 (counter.go:18) CALL runtime.racefuncexit(SB)
0x00c0 00192 (counter.go:18) MOVQ 104(SP), BP
0x00c5 00197 (counter.go:18) ADDQ $112, SP
コンパイルされたコードには、runtime.racefuncenter、runtime.raceread、runtime.racewrite、runtime.racefuncexitなどのデータ競合を検出するメソッドが追加されています。これらの挿入されたコマンドを使用して、Go競合検出ツールはデータ競合の問題を正常に検出できます。要約すると、コンパイル時にいくつかの命令を挿入し、実行時にこれらの挿入された命令を介して同時読み取りと書き込みを検出し、データ競合の問題を発見することにより、これがこのツールの実装メカニズムです。