dongwei3712 2019-08-06 03:43 采纳率: 100%
浏览 417
已采纳

为什么这里有通道可变输出

I am trying code modified from here. I create 5 channels and send data 5 times:

package main
import "fmt"
func greet(c chan string) {
    fmt.Println("Hello " + <-c + "!")
}
func main() {
    fmt.Println("main() started")
    c := make(chan string)
    for i:=0; i<5; i++ {
        go greet(c)
    }
    c <- "AAA"
    c <- "BBB"
    c <- "CCC"
    c <- "DDD"
    c <- "EEE"
    fmt.Println("main() stopped")
}

I expected all 5 strings to be printed. However, I find variable output. Some outputs are:

$ ./rnchannel
main() started
Hello AAA!
Hello DDD!
Hello BBB!
Hello CCC!
Hello EEE!
main() stopped

$ ./rnchannel
main() started
Hello CCC!
Hello DDD!
main() stopped

$ ./rnchannel
main() started
Hello CCC!
Hello BBB!
Hello AAA!
Hello DDD!
main() stopped

Why variable number of lines are being printed?

  • 写回答

2条回答 默认 最新

  • drnrxv9383 2019-08-06 04:31
    关注

    @AdamSmith identified the problem. When main() exits all goroutines are killed. There's no guarantee your other goroutines will finish before that, that's the nature of concurrency. Here's how you fix it.

    First, let's make a couple changes to greet. Have it sleep just a moment to make the problem more pronounced. We'll also have it accept a string rather than the channel, we'll see why in a moment.

    func greet(str string) {
        time.Sleep(100 * time.Millisecond)
        fmt.Println("Hello " + str + "!")
    }
    

    Rather than creating a bunch of goroutines to read a fixed number of times from a channel, we want one goroutine which reads from the channel until it's exhausted. This is easiest done using range. This takes full advantage of channels.

    We also need a way to tell the main program to wait until the loop is done. This is easiest done with a second channel. More complex synchronization uses WaitGroups.

    c := make(chan string, 2)
    done := make(chan bool, 1)
    go func() {
        for str := range(c) {
            greet(str)
        }
        done <- true
    }()
    

    The goroutine will read from c until it's closed. Then it will send true to the channel done. Both are buffered to avoid deadlock due to blocking waiting to read or write to the channel.

    Back in main, we write to the channel, explicitly close it, and then wait to read from done.

        c <- "AAA"
        c <- "BBB"
        c <- "CCC"
        c <- "DDD"
        c <- "EEE"
        close(c)
    
        <-done
        fmt.Println("main() stopped")
    

    <-done will block until there is something to read. This allows the goroutine to finish.

    And bringing it all together.

    package main
    import(
        "fmt"
        "time"
    )
    func greet(str string) {
        time.Sleep(100 * time.Millisecond)
        fmt.Println("Hello " + str + "!")
    }
    func main() {
        fmt.Println("main() started")
        c := make(chan string, 2)
        done := make(chan bool, 1)
        go func() {
            for str := range(c) {
                greet(str)
            }
            done <- true
        }()
        c <- "AAA"
        c <- "BBB"
        c <- "CCC"
        c <- "DDD"
        c <- "EEE"
        close(c)
    
        <-done
        fmt.Println("main() stopped")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 Vs code Mac系统 PHP Debug调试环境配置
  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集
  • ¥15 在启动roslaunch时出现如下问题
  • ¥15 汇编语言实现加减法计算器的功能
  • ¥20 关于多单片机模块化的一些问题
  • ¥30 seata使用出现报错,其他服务找不到seata
  • ¥35 引用csv数据文件(4列1800行),通过高斯-赛德尔法拟合曲线,在选取(每五十点取1点)数据,求该数据点的曲率中心。
  • ¥20 程序只发送0X01,串口助手显示不正确,配置看了没有问题115200-8-1-no,如何解决?
  • ¥15 Google speech command 数据集获取