go - 无法无限期地写入一个 goroutine 并从另一个 goroutine 中读取

标签 go goroutine

我正在学习围棋,但在使用 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/

相关文章:

go - 如何使用 Golang 将文件添加到现有的 zip 文件

go - Golang中的类型转换

go - 使用 Goroutine 订阅 MQTT 消息

go - 关于golang channel 的一些问题

go - 如何在 3 秒内打印此 Go 代码?

api - 从 Go 中的接口(interface)访问数据

反射式没有方法

regex - golang使用正则表达式从键=值对字符串中提取唯一键、值

concurrency - 为什么这段 Go 代码会死锁?

go - 让 golang 在所有 goroutines 完成后关闭使用的 channel