为什么在下面的情况下,当使用两个goroutine时,我的struct值不更新?

为什么在下面的情况下,当使用两个goroutine时,我的struct值不更新?,go,struct,goroutine,Go,Struct,Goroutine,所以,我是个新手,上周末开始学习这门语言,并为RPG创建了一个基本的角色生成器/自动注册器。 由于生成器在达到某些值之前会进行大量随机数滚动,因此我认为可以使用goroutines来加快处理速度。 这是我迄今为止所做的最大努力,但是在最终的Printf期间,我总是得到STR:0 DEX:0,尽管我在调试期间可以看到两个并行函数正确地设置了值。 myCharacter结构是在它们之外声明的,所以我认为更新它应该可以正常工作 这是我糟糕的代码。我试图实现的是在并行goroutine中滚动STR和DE

所以,我是个新手,上周末开始学习这门语言,并为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
}

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

声明互斥体:

myCharacter := new(Character)
mutex:=sync.Mutex{}
读/写共享变量时使用互斥锁:

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


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

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

这可以通过使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运行。下一步是不要剪切粘贴代码,而是使用循环

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

请注意,如果您期望两个值都为1000000,则需要10000000000次尝试1万亿次。两个goroutine不会使速度快很多。

您能解释一下您的代码应该做什么吗?看起来你正在生成从1到1000000的随机数,如果它们在两个重复的goroutine中不是都是1000000,就拒绝它们。目的是什么?@Schwern嗯,这是唯一一个用来学习如何进行goroutine的模拟代码,我会用一些计时函数来测试性能。我需要它运行很多次,所以我为这两个值选择了1000000。事实上,如果mySTR>=minSTR&&myDEX>=minDEX,我将滚动2d6 so rand.Intn6+rand.Intn6+2,并根据CLI标志(如-minSTR和-minDEX)检查统计数据。您可以在这里看到我的实际生成器:对于像生成随机数和检查边界这样简单的事情,goroutine之间的所有切换可能会使它变慢,但可以尝试一下。考虑一下有一个GROUTIN,它给频道提供有效的随机数,其他的东西都从那个频道读取。这避免了Burak提到的那种同步问题。我会试试看,@Schwern,这也是学习goroutines的一个很好的练习虽然我需要对随机数生成部分进行并行化改进,但基本上是对掷骰子部分进行并行化,而不是检查部分。也许两个goroutine将随机结构放入一个通道中,然后一个侦听器根据边界检查它们的属性?感谢您提供的信息-仅从这一点,我将为未来学习。但是,如前所述修改我的代码仍然会导致最后打印“0”。我像你建议的那样,在goroutine和Printf周围添加了互斥锁。就是这样!老兄,我太蠢了,我掉进了我在YouTube上观看的教程中明确提到的陷阱。我已经在想为什么程序这么快就退出了。甚至我在QuitChan频道向另一个goroutine发送消息也能如期工作。多谢各位@查斯卡:这是一个很常见的陷阱。
    for i:= 0; i < 2; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                myCharacter.STR = 42;
                myCharacter.DEX = 23;
                break;
            }
        }()
    }