Go 中的通道 / Go 例程同步问题

这是一个包含我正在尝试使用的基本架构/流程的小示例程序。如何打印所有数字和“结束”消息?我试过在这里和那里放置关闭语句,但它要么不起作用,要么我对尝试关闭已经关闭的频道感到恐慌......


package main


import (

    "fmt"

    "time"

)


func main() {

    d := make(chan uint)


    go bar(d)


    c1 := make(chan uint)

    c2 := make(chan uint)

    c3 := make(chan uint)


    go foo(c1, d)

    go foo(c2, d)

    go foo(c3, d)


    c1 <- 1

    c2 <- 2

    c3 <- 3


    c1 <- 4

    c2 <- 5

    c3 <- 6


    c1 <- 7

    c2 <- 8

    c3 <- 9

}


func foo(c chan uint, d chan uint) {

    fmt.Println("foo start")


    for stuff := range c {

        time.Sleep(1)

        d <- stuff * 2

    }


    fmt.Println("foo end")

}


func bar(d chan uint) {

    fmt.Println("bar start")


    for stuff := range d {

        fmt.Printf("bar received %d\n", stuff)

    }


    fmt.Println("bar end")

}

我得到的输出看起来像这样。请注意最后一组数字和“结束”输出丢失。


foo start

bar start

foo start

foo start

bar received 6

bar received 2

bar received 4

bar received 12

bar received 8

bar received 10

在我的实际程序中,每个“foo”函数都在进行过滤和一堆繁重的字符串正则表达式。而且我需要“bar”功能,因为它具有根据时间戳重新排序和序列化打印的工作,因此输出不会交错。


拉莫斯之舞
浏览 179回答 2
2回答

慕哥9229398

你的程序在所有 goroutine 完成之前就退出了。在从 返回之前,您需要等待 thefoo和bargoroutines 完成main。执行此操作的通常方法是使用 a sync.WaitGroup,但由于main它不是d通道的生产者,因此您必须确保在使用第二个 WaitGroup(或等效项)关闭该通道之前完成该通道上的所有发送。var (&nbsp; &nbsp; fooWG sync.WaitGroup&nbsp; &nbsp; barWG sync.WaitGroup)func main() {&nbsp; &nbsp; d := make(chan uint)&nbsp; &nbsp; barWG.Add(1)&nbsp; &nbsp; go bar(d)&nbsp; &nbsp; c1 := make(chan uint)&nbsp; &nbsp; c2 := make(chan uint)&nbsp; &nbsp; c3 := make(chan uint)&nbsp; &nbsp; fooWG.Add(3)&nbsp; &nbsp; go foo(c1, d)&nbsp; &nbsp; go foo(c2, d)&nbsp; &nbsp; go foo(c3, d)&nbsp; &nbsp; c1 <- 1&nbsp; &nbsp; c2 <- 2&nbsp; &nbsp; c3 <- 3&nbsp; &nbsp; c1 <- 4&nbsp; &nbsp; c2 <- 5&nbsp; &nbsp; c3 <- 6&nbsp; &nbsp; c1 <- 7&nbsp; &nbsp; c2 <- 8&nbsp; &nbsp; c3 <- 9&nbsp; &nbsp; // close the channels so the foo goroutines can exit&nbsp; &nbsp; close(c1)&nbsp; &nbsp; close(c2)&nbsp; &nbsp; close(c3)&nbsp; &nbsp; fooWG.Wait()&nbsp; &nbsp; // all foo are done, so it's safe to close d and wait for bar&nbsp; &nbsp; close(d)&nbsp; &nbsp; barWG.Wait()}func foo(c chan uint, d chan uint) {&nbsp; &nbsp; defer fooWG.Done()&nbsp; &nbsp; fmt.Println("foo start")&nbsp; &nbsp; for stuff := range c {&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(1)&nbsp; &nbsp; &nbsp; &nbsp; d <- stuff * 2&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("foo end")}func bar(d chan uint) {&nbsp; &nbsp; defer barWG.Done()&nbsp; &nbsp; fmt.Println("bar start")&nbsp; &nbsp; for stuff := range d {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("bar received %d\n", stuff)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("bar end")}

慕的地8271018

JimB 的答案绝对有效,但它增加了比代码中实际需要的更多的复杂性。一个简单的完整通道就足以通过完成同步此代码。此外,通过通道同步,time.Sleep(1)功能不再需要该命令:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "time")func main() {&nbsp; &nbsp; d := make(chan uint)&nbsp; &nbsp; complete := make(chan bool)&nbsp; &nbsp; go bar(d, complete)&nbsp; &nbsp; c1 := make(chan uint)&nbsp; &nbsp; c2 := make(chan uint)&nbsp; &nbsp; c3 := make(chan uint)&nbsp; &nbsp; go foo(c1, d)&nbsp; &nbsp; go foo(c2, d)&nbsp; &nbsp; go foo(c3, d)&nbsp; &nbsp; c1 <- 1&nbsp; &nbsp; c2 <- 2&nbsp; &nbsp; c3 <- 3&nbsp; &nbsp; c1 <- 4&nbsp; &nbsp; c2 <- 5&nbsp; &nbsp; c3 <- 6&nbsp; &nbsp; c1 <- 7&nbsp; &nbsp; c2 <- 8&nbsp; &nbsp; c3 <- 9&nbsp; &nbsp; //If you know the number of inputs, count them to ensure completion&nbsp; &nbsp; for i:=0; i < 9; i++{&nbsp; &nbsp; &nbsp; &nbsp; <-complete&nbsp; &nbsp; }&nbsp; &nbsp; //Clean up after yourself, to keep away the memory leaks&nbsp; &nbsp; close(c1)&nbsp; &nbsp; close(c2)&nbsp; &nbsp; close(c3)&nbsp; &nbsp; close(d)&nbsp; &nbsp; //Verify bar is done and closed correctly&nbsp; &nbsp; <-complete&nbsp; &nbsp; close(complete)}func foo(c chan uint, d chan uint) {&nbsp; &nbsp; fmt.Println("foo start")&nbsp; &nbsp; for stuff := range c {&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(1)&nbsp; &nbsp; &nbsp; //Not needed for the program to function&nbsp; &nbsp; &nbsp; &nbsp; d <- stuff * 2&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("foo end")}func bar(d chan uint, cmp chan bool) {&nbsp; &nbsp; fmt.Println("bar start")&nbsp; &nbsp; for stuff := range d {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("bar received %d\n", stuff)&nbsp; &nbsp; &nbsp; &nbsp; cmp <- true&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("bar end")&nbsp; &nbsp; //verify that cmp can be closed (all output is done, and d is closed)&nbsp; &nbsp; cmp <- true}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go