ホームページ > バックエンド開発 > Golang > この Go コードでは、「One」、「Two」、「Three」ではなく「Three」が 3 回表示されるのはなぜですか?

この Go コードでは、「One」、「Two」、「Three」ではなく「Three」が 3 回表示されるのはなぜですか?

DDD
リリース: 2024-12-09 13:41:11
オリジナル
952 人が閲覧しました

Why Does This Go Code Print

Goroutine の動作: 謎を解く

提供された Go コードで、次のような複雑な動作に遭遇します。

package main

import (
    "fmt"
    "time"
)

type field struct {
    name string
}

func (p *field) print() {
    fmt.Println(p.name)
}

func main() {
    data := []field{{"one"}, {"two"}, {"three"}}
    for _, v := range data {
        go v.print()
    }
    <-time.After(1 * time.Second)
}
ログイン後にコピー

疑問が生じます:なぜこのコードは常に「three」を表示せずに 3 回出力するのでしょうか。 「1 つ」、「2 つ」、「3 つ」は順不同ですか?

問題の理解

問題の核心は、次のような原因で引き起こされる微妙な競合状態にあります。 goroutine 関数での範囲変数 v の使用。

v.print() を記述するとき、事実上変数 v へのポインタを渡していることになります。これは、範囲データ ループ内の現在の要素への参照です。ただし、ループは反復を続け、v の値を変更します。

ゴルーチンが実行されると、v の最終値は "three" になります。これにより、3 つの「three」という予期しない出力が生成されます。

問題の解決: 複数のアプローチ

この問題を解決するには、いくつかの方法があります:

1.短い変数宣言の使用:

ループの各反復をスコープとする新しい変数 v を作成します:

for _, v := range data {
    v := v // Short variable declaration to create a new `v`.
    go v.print()
}
ログイン後にコピー

2.ポインタのスライスの使用:

データのタイプをポインタのスライスに変更し、個々のポインタを goroutine 関数に渡します:

data := []*field{{"one"}, {"two"}, {"three"}} // Note the '*'
for _, v := range data {
    go v.print()
}
ログイン後にコピー

3.スライス要素のアドレスを使用する:

各スライス要素のアドレスを取得し、ポインタを goroutine 関数に渡します:

data := []*field{{"one"}, {"two"}, {"three"}} // Note the '*'
for i := range data {
    v := &data[i]
    go v.print()
}
ログイン後にコピー

結論

ループが範囲変数の値を変更する場合、範囲変数のアドレスを取得すると、ゴルーチンで予期しない動作が発生する可能性があることに注意してください。変数。上記で概説した手法を使用すると、各ゴルーチンが確実に一意の値を受け取り、データ競合の問題を回避できます。

以上がこの Go コードでは、「One」、「Two」、「Three」ではなく「Three」が 3 回表示されるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート