为什么在以下情况下使用两个 goroutine 时我的结构值不更新?

Why do my struct values not update in the following case when using two goroutines?

所以,我是 Go 的新手,上周末开始学习这门语言,并为 RPG 创建了一个基本的字符生成器/自动滚动器。 由于生成器会进行大量随机数滚动,直到达到某些值,我想我可能会使用 goroutines 来加快处理速度。 到目前为止,这是我最大的努力,但不知何故,在最终的 Printf 期间,我总是得到“STR: 0 DEX: 0”,即使我在调试期间可以看到两个并行函数正在正确设置值。 myCharacter 结构是在它们之外声明的,所以我认为更新它应该可以正常工作?

这是我糟糕的代码。我想要实现的是在并行 goroutine 中滚动“STR”和“DEX”,直到其中一个达到任意条件(这里的值都是 1000000,只是作为测试)。

任何人都可以帮我解决为什么 myCharacter.STR 和 myCharacter.DEX 最后打印为 0?

package main

import (
    "fmt"
    "math/rand"
    "time"
)

type Character struct {
    STR int
    DEX int
}

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    QuitChan := make(chan bool)

    myCharacter := new(Character)
    go func() {
        for {
            select {
            case <-QuitChan:
                return
            default:
                mySTR, myDEX := RollChar()
                if mySTR >= 1000000 && myDEX >= 1000000 {
                    myCharacter.STR = mySTR
                    myCharacter.DEX = myDEX
                    QuitChan <- true
                    return
                }
            }
        }
    }()
    go func() {
        for {
            select {
            case <-QuitChan:
                return
            default:
                mySTR, myDEX := RollChar()
                if mySTR >= 1000000 && myDEX >= 1000000 {
                    myCharacter.STR = mySTR
                    myCharacter.DEX = myDEX
                    QuitChan <- true
                    return
                }
            }
        }
    }()
    fmt.Printf("STR: %d DEX: %d", myCharacter.STR, myCharacter.DEX)
}

func RollChar() (int, int) {
    mySTR := rand.Intn(1000000) + 1
    myDEX := rand.Intn(1000000) + 1
    return mySTR, myDEX
}

您有一场数据竞赛。您的两个 goroutines 正在访问共享变量而没有任何显式同步。在这种情况下,您需要使用互斥锁来确保互斥以及在一个 goroutine 中所做的更改在其他 goroutine 中的可见性。

声明一个互斥量:

myCharacter := new(Character)
mutex:=sync.Mutex{}

在 read/write 共享变量时使用互斥体:

mutex.Lock()
myCharacter.STR = mySTR
myCharacter.DEX = myDEX
mutex.Unlock()

还有:

mutex.Lock()
fmt.Printf("STR: %d DEX: %d", myCharacter.STR, myCharacter.DEX)
mutex.Unlock()

没有互斥量,就无法保证对共享变量所做的修改对其他 goroutine 可见。

你的 goroutines 永远不会 运行。 main 不等待 goroutines 完成,它在执行之前打印并退出。

这可以通过 WaitGroups 来解决,让 main 等待所有 goroutine 完成。它基本上是一个花哨的计数器。

    // Create the WaitGroup
    var wg sync.WaitGroup

    // Add one thing to wait for.
    // This must be done outside the goroutine to ensure it's added
    // before `wg.Wait()` is called.
    wg.Add(1)
    go func() {
        # When the goroutine exits, say this one thing is done.
        defer wg.Done()
        for {
            ...
        }
    }()

    // Same for the next one.
    wg.Add(1)
    go func() {
        defer wg.Done()
        for {
            ...
        }
    }()

    // Wait until the two things in the WaitGroup are done.
    wg.Wait()
    
    fmt.Printf("STR: %d DEX: %d", myCharacter.STR, myCharacter.DEX)

这会让你的 goroutines 运行ning。接下来是不剪切&粘贴代码,而是使用循环。

    for i:= 0; i < 2; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                myCharacter.STR = 42;
                myCharacter.DEX = 23;
                break;
            }
        }()
    }

请注意,如果您希望两个值均为 1,000,000,则需要尝试 1,000,000,000,000 次(1 万亿)。两个 goroutines 不会使那么快。