私はGo言語の初心者なので、質問が非常に基本的なものである場合は失礼します。私は非常に単純なコードを書きました:
func main(){
var count int // Default 0
cptr := &count
go incr(cptr)
time.Sleep(100)
fmt.Println(*cptr)
}
// Increments the value of count through pointer var
func incr(cptr *int) {
for i := 0; i < 1000; i++ {
go func() {
fmt.Println(*cptr)
*cptr = *cptr + 1
}()
}
}
countの値は、ループの実行回数の1だけ増加する必要があります。以下のケースを検討してください。
ループは100回実行されます-> countの値は100です(ループが100回実行されるため、これは正しいことです)。
ループは> 510回実行されます->カウントの値は508または510のいずれかです。これは100000であっても発生します。
私はこれを8コアプロセッサマシンで実行しています。
まず、Go 1.5より前のバージョンでは、シングルプロセッサで実行され、システムコールをブロックするために複数のスレッドのみを使用します。GOMAXPROCSを使用してより多くのプロセッサを使用するようランタイムに指示しない限り。
Go 1.5以降、GOMAXPROCSはCPUの数に設定されます。参照6、7。
また、操作*cptr = *cptr + 1
がアトミックであることは保証されていません。注意深く見ると、3つの操作に分けることができます。ポインタの逆参照による古い値のフェッチ、値の増分、ポインタアドレスへの値の保存です。
508/510を取得しているという事実は、ランタイムの魔法によるものであり、そのようにとどまるように定義されていません。同時実行操作の動作の詳細については、Goメモリモデルを参照してください。
510未満の開始されたゴルーチンの正しい値を取得している可能性があります。これは、これらの値を下回る数値が(まだ)中断されていないためです。
一般に、実行しようとしていることは、どの言語でも推奨できず、同時実行を行う「実行」方法でもありません。:同期するためにチャネルを使用して、非常に良い例では、このコード徒歩共有メモリーて通信(むしろ共有メモリでの通信より)
これが私が何を意味するかを示すための小さな例です:バッファを1にしてチャネルを使用して現在の数を保存し、必要なときにチャネルからフェッチし、自由に変更して、他の人が使用できるように戻します。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加