ホームページ  >  記事  >  バックエンド開発  >  Go言語のリソース競合問題について語る記事

Go言語のリソース競合問題について語る記事

青灯夜游
青灯夜游転載
2023-02-17 14:40:203239ブラウズ

Go言語のリソース競合問題について語る記事

スレッド セーフが同時プログラミングにおいて非常に重要であることは誰もが知っています。次に、スレッドのセキュリティが確保されていない状況を再現するシナリオを想定し、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言語のリソース競合問題について語る記事上記のコードの実行結果は実際には問題ありませんが、なぜ乱れが生じるのでしょうか。 ?これは 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 ただ。もちろん、これが 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)
    }
}

同時実行数が比較的少ない場合、この問題は発生しない可能性があります。同時実行数が大きくなると、問題が発生します。すぐに

画像の実行結果は Go言語のリソース競合問題について語る記事同時マップ書き込みエラー

なぜこの問題が発生するのでしょうか? 100人がカゴに果物を入れると仮定しますが、これは簡単です。しかし、100 人がカゴから果物を取る場合、問題が発生します。第一に、カゴに十分な果物がない可能性があること、第二に、誰もが最初にそれを取りたがるので、必然的に競争が発生することです。

問題 1 最適化

上記の問題を考慮して、グローバル ロックの概念を導入します。これは、私たちがトイレに行くときと似ています。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 になるのは、保存できないデータ型が原因である可能性がありますが、気にする必要はありませんGo言語のリソース競合問題について語る記事

このようにして、資源競争の問題を解決します。しかし、実際には別の問題があり、メイン コルーチンで依然として手動で待機する必要があり、これは非常に悪いことです。サブ コルーチンが 3 秒以内に問題を解決できなかったらどうなるでしょうか? Go言語のリソース競合問題について語る記事

問題 2 最適化Go言語のリソース競合問題について語る記事

この問題は、メイン コルーチン内のサブコルーチンを手動で待機したくないことです。コード内で直接待機時間を指定する必要はありません。ここでは、

WaitGroup

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 サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。