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

  • 本文关键字: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 %dn", stuff)
    }
    fmt.Println("bar end")
}

我得到的输出看起来像这样。注意,最后一组数字和"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"函数,因为它的工作是根据时间戳重新排序,并序列化打印,这样输出就不会交错。

您的程序在所有例程完成之前退出。在从main返回之前,需要等待foobar的程序都完成。

通常的方法是使用sync.WaitGroup,但由于main不是d通道的生产者,您必须确保该通道上的所有发送都在使用第二个WaitGroup(或等效的)关闭之前完成。

var (
    fooWG sync.WaitGroup
    barWG sync.WaitGroup
)
func main() {
    d := make(chan uint)
    barWG.Add(1)
    go bar(d)
    c1 := make(chan uint)
    c2 := make(chan uint)
    c3 := make(chan uint)
    fooWG.Add(3)
    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
    // close the channels so the foo goroutines can exit
    close(c1)
    close(c2)
    close(c3)
    fooWG.Wait()
    // all foo are done, so it's safe to close d and wait for bar
    close(d)
    barWG.Wait()
}
func foo(c chan uint, d chan uint) {
    defer fooWG.Done()
    fmt.Println("foo start")
    for stuff := range c {
        time.Sleep(1)
        d <- stuff * 2
    }
    fmt.Println("foo end")
}
func bar(d chan uint) {
    defer barWG.Done()
    fmt.Println("bar start")
    for stuff := range d {
        fmt.Printf("bar received %dn", stuff)
    }
    fmt.Println("bar end")
}

JimB的答案确实有效,但它增加了代码中实际需要的更多复杂性。一个简单的完整通道就足以通过完成来同步此代码。

此外,使用通道同步,不再需要time.Sleep(1)命令来实现功能:

package main
import (
    "fmt"
    "time"
)
func main() {
    d := make(chan uint)
    complete := make(chan bool)
    go bar(d, complete)
    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
    //If you know the number of inputs, count them to ensure completion
    for i:=0; i < 9; i++{
        <-complete
    }
    //Clean up after yourself, to keep away the memory leaks
    close(c1)
    close(c2)
    close(c3)
    close(d)
    //Verify bar is done and closed correctly
    <-complete
    close(complete)
}
func foo(c chan uint, d chan uint) {
    fmt.Println("foo start")
    for stuff := range c {
        time.Sleep(1)      //Not needed for the program to function
        d <- stuff * 2
    }
    fmt.Println("foo end")
}
func bar(d chan uint, cmp chan bool) {
    fmt.Println("bar start")
    for stuff := range d {
        fmt.Printf("bar received %dn", stuff)
        cmp <- true
    }
    fmt.Println("bar end")
    //verify that cmp can be closed (all output is done, and d is closed)
    cmp <- true
}

最新更新