スレッド セーフが同時プログラミングにおいて非常に重要であることは誰もが知っています。次に、スレッドのセキュリティが確保されていない状況を再現するシナリオを想定し、Go で
1~100 を実行する必要があります。階乗を見つけて結果をマップに入れます
1! = 1 = 1 2! = 1 * 2 = 2 3! = 1 * 2 * 3 = 6 4! = 1 * 2 * 3 * 4 = 24 5! = 1 * 2 * 3 * 4 * 5 = 120 ... { 1: 1 2: 2 3: 6 4: 24 5: 120 ... }
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 10; i++ { Factorial(i) } for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
上記のコードの実行結果は実際には問題ありませんが、なぜ乱れが生じるのでしょうか。 ?これは Go 言語のマップなので、実際には順序が狂っていて、私たちの理解では、最初に格納され、最初に出力されるのですが、申し訳ありませんが、Golang のマップはこのようなものではありません。 上記の実行に問題はありません。慎重な学生は、このバージョンのコードでは同時実行性が使用されていないことに気づいたかもしれませんね?さて、改善を続けましょう
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 10; i++ { go Factorial(i) } for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
同時実行バージョンでは階乗を計算する呼び出しの前に ## が追加されていることがわかります。 #go ただ。もちろん、これが go 言語でコルーチンを開始するためのキーワードであることは誰もが知っています。
実行結果はコンソールに何も出力されませんが、これはメインコルーチンとサブコルーチンの実行関係によるものです。絵を描いて理解してみましょう
上の図から、メイン コルーチンの実行時間は短く (比較的短く表示)、サブコルーチンの実行時間は比較的長い (比較的長く表示) ことがわかります。 サブ コルーチンは現在のメイン コルーチンに相対的なものであることを覚えておく必要があります。メイン コルーチンが存在しない場合、サブ コルーチンは存在しません
したがって、上記のコードは何も出力しません。実行されましたが、サブコルーチンは完了していません。サブコルーチンが完了していない場合、factorialMap
には何かありますか?メイン待機サブ
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 100; i++ { go Factorial(i) } time.Sleep(time.Second * 3) for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
同時実行数が比較的少ない場合、この問題は発生しない可能性があります。同時実行数が大きくなると、問題が発生します。すぐに
画像の実行結果は 同時マップ書き込みエラー
なぜこの問題が発生するのでしょうか? 100人がカゴに果物を入れると仮定しますが、これは簡単です。しかし、100 人がカゴから果物を取る場合、問題が発生します。第一に、カゴに十分な果物がない可能性があること、第二に、誰もが最初にそれを取りたがるので、必然的に競争が発生することです。問題 1 最適化
#
var factorialMap = make(map[int]int) var lock sync.Mutex func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } // defer 不好理解 // defer func(){ // lock.Unlock() // 执行完解锁 // }() lock.Lock() // 执行时上锁 factorialMap[n] = result lock.Unlock() // 执行后解锁 } func main() { for i := 1; i < 100; i++ { go Factorial(i) } time.Sleep(time.Second * 3) for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
実行結果が 0 になるのは、保存できないデータ型が原因である可能性がありますが、気にする必要はありません
このようにして、資源競争の問題を解決します。しかし、実際には別の問題があり、メイン コルーチンで依然として手動で待機する必要があり、これは非常に悪いことです。サブ コルーチンが 3 秒以内に問題を解決できなかったらどうなるでしょうか?
問題 2 最適化
var factorialMap = make(map[int]int) var lock sync.Mutex var wg sync.WaitGroup func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } lock.Lock() // 执行时上锁 factorialMap[n] = result lock.Unlock() // 执行后解锁 wg.Done() } func main() { for i := 1; i < 100; i++ { wg.Add(1) go Factorial(i) } wg.Wait() for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
の内部原則を紹介しました。詳細については、こちらをご覧ください。私は勝ちました。今はそこに入らないでください
要約すると、WaitGroup
はバスケットです。コルーチンが開かれるたびに、識別子 (Add 関数) がバスケットに追加されます。コルーチンが実行されるたびに、識別子 (Done) がバスケットから減算されます。バスケット.関数)、最後にバスケットを確認します。バスケットが空の場合は、コルーチンが実行されたことを意味します (待機関数)
[推奨学習: ビデオ チュートリアルに進む
]
以上がGo言語のリソース競合問題について語る記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。