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