ホームページ  >  記事  >  バックエンド開発  >  Golang関数のグローバル変数とローカル変数のデータ競合分析

Golang関数のグローバル変数とローカル変数のデータ競合分析

WBOY
WBOYオリジナル
2023-05-21 08:19:35762ブラウズ

Golang は、効率性、シンプルさ、同時実行性の特徴を備えた強力に型指定されたプログラミング言語であるため、徐々に多くの開発者に好まれています。 Golang の開発では、関数のグローバル変数とローカル変数がデータ競合の問題を引き起こすことがよくあります。この記事では、Golang関数におけるグローバル変数とローカル変数のデータ競合問題を実際のコーディングの観点から分析していきます。

1. グローバル変数のデータ競合

Golang のグローバル変数はすべての関数からアクセスできるため、厳密な設計とコーディングを行わないとデータ競合の問題が発生しやすくなります。

たとえば、次のコードでは、グローバル変数 num を定義し、2 つの異なる関数でそれをインクリメントします。

var num int = 0

func addNum1() {
    for i := 0; i < 1000; i++ {
        num += 1
    }
}

func addNum2() {
    for i := 0; i < 1000; i++ {
        num += 1
    }
}

上記のコードでは、両方の関数でグローバル変数 num が増加します。データ競合の問題が発生する可能性があります。データ競合は、少なくとも 1 つのスレッドがリソースに書き込みを行っているときに 2 つ以上のスレッドが同じ共有リソースに同時にアクセスすると発生し、未定義の動作が発生します。

この問題を解決する方法は、Golang が提供する同期パッケージの Mutex タイプを使用することです。ミューテックスはミューテックス ロックであり、ロックを保持しているスレッドのみが共有リソースにアクセスできます。修正コードは次のとおりです。

var num int = 0
var mutex sync.Mutex

func addNum1() {
    for i := 0; i < 1000; i++ {
        mutex.Lock()
        num += 1
        mutex.Unlock()
    }
}

func addNum2() {
    for i := 0; i < 1000; i++ {
        mutex.Lock()
        num += 1
        mutex.Unlock()
    }
}

上記の修正コードでは、Mutex を介してグローバル変数 num への相互排他アクセスを実装し、データ競合の問題を回避しています。

2. ローカル変数のデータ競合

ローカル変数は関数内で定義され、関数内でのみアクセスできるため、発生する可能性のあるデータ競合の問題は比較的まれです。ただし、ローカル変数を使用する場合には注意が必要な問題がまだいくつかあります。

たとえば、次のコードでは、関数 getRandStr は長さ 10 のランダムな文字列を返します。

import (
    "math/rand"
    "time"
)

func getRandStr() string {
    rand.Seed(time.Now().UnixNano())
    baseStr := "abcdefghijklmnopqrstuvwxyz0123456789"
    var randBytes []byte
    for i := 0; i < 10; i++ {
        randBytes = append(randBytes, baseStr[rand.Intn(len(baseStr))])
    }
    return string(randBytes)
}

上記のコードでは、ランダムな計算により 10 桁の長さを生成しました。ランダムな文字列に番号を付け、それを戻り値として使用します。このようなコードにはデータ競合の問題がないように見えますが、実際には、複数のゴルーチンで同時に呼び出された場合、rand.Seed(time.Now().UnixNano()) のパラメータが時間とともに変化することを考慮すると、この関数は関数が同じ結果を返し、競合問題が発生する可能性があります。

この問題を解決するには、rand.Seed(time.Now().UnixNano()) を関数の外に抽出し、プログラムの実行中に一度呼び出すだけで済みます。

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UnixNano())
}

func getRandStr() string {
    baseStr := "abcdefghijklmnopqrstuvwxyz0123456789"
    var randBytes []byte
    for i := 0; i < 10; i++ {
        randBytes = append(randBytes, baseStr[rand.Intn(len(baseStr))])
    }
    return string(randBytes)
}

上記の変更されたコードでは、複数の呼び出しを避けるために、init 関数を通じて rand.Seed(time.Now().UnixNano()) を 1 回だけ呼び出します。 goroutine内でこの関数を同時に呼び出すとデータの競合が発生します。

3. 結論

上記は、Golang 関数におけるグローバル変数とローカル変数のデータ競合問題の分析ですが、要約すると、次の原則に従う必要があります。

    グローバル変数の読み取りおよび書き込みを行う場合は、ミューテックス ロックを使用して、グローバル変数への相互排他的アクセスを実現します。
  1. ローカル変数を使用する場合、関数の外部で乱数生成器を使用する場合は、複数の goroutine による同時アクセスによって引き起こされるデータ競合の問題を回避するために初期化が必要であることに注意してください。
上記の原則に従うことで、Golang 関数におけるデータ競合の問題を回避できます。

以上がGolang関数のグローバル変数とローカル変数のデータ競合分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。