我正在学习围棋,但在使用 goroutines
时遇到了问题。这是我的代码
package main
import (
"fmt"
"sync"
"time"
)
var counter = 0
var wg = sync.WaitGroup{}
func main() {
ticker := time.NewTicker(time.Second)
go func() {
for range ticker.C {
// wg.Add(1)
// defer wg.Done()
counter++
fmt.Println(counter)
//wg.Done()
}
}()
ticker2 := time.NewTicker(time.Second * 2)
wg.Add(1)
go func() {
for range ticker2.C {
//defer wg.Done()
fmt.Println(counter)
}
}()
wg.Wait()
}
基本上,我想要:
- 一个名为
counter
的全局变量 - 一个
goroutine
每 1 秒更新一次这个计数器 - 另一个
goroutine
每两秒打印一次这个计数器
Playground 是 here
我试着玩 WaitGroup
但我没能成功。
对于这个级别的代码,我有以下警告:
WARNING: DATA RACE
Read at 0x0000011d8318 by goroutine 8:
runtime.convT2E64()
另一个问题这个线程安全吗?我的意思是,我可以在两个 groroutine 之外的 main 方法中安全地使用计数器吗?
最佳答案
一种方法可能是在需要打印时向第二个 gorutine 发送消息。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Start");
counter := 0
printChannel := make( chan int)
done := make(chan struct{} )
go func(done <-chan struct{}, printChannel chan<- int ){
timer := time.NewTicker(time.Second)
bDone := false;
for !bDone {
select {
case <-timer.C:
counter++
if counter%2 == 0 {
printChannel <- counter
}
case <-done:
bDone=true
}
}
}(done, printChannel)
go func(done <-chan struct{}, printChannel <-chan int ){
bDone:=false
for !bDone{
select {
case n := <-printChannel:
fmt.Print(n, " ");
case <-done:
bDone=true
}
}
}(done,printChannel)
//whatever logic to stop
go func() {
<- time.After(21*time.Second)
done <- struct{}{}
}()
<-done
fmt.Println("\nEnd");
}
注意这里有第三个 gorutine 来完成这个例子
输出:
Start
2 4 6 8 10 12 14 16 18 20
End
要改进它,您可以在没有 bDone 变量的情况下进行,您可以停止 Ticker 并在 gorutine 存在时添加适当的消息。
或者你可能想用标签测试'break'以退出循环。
您可以使用关闭(完成) channel 而不是向其发送消息来测试效果。
此外,如果您准备放宽读/写顺序,您可以删除 printChannel 并让第二个 gorutine 使用另一个每 2 秒 ping 一次的自动收报机。
关于go - 无法无限期地写入一个 goroutine 并从另一个 goroutine 中读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49617904/