为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
拉莫斯之舞 2021-11-01 17:01:31
这是一个包含我正在尝试使用的基本架构/流程的小示例程序。如何打印所有数字和“结束”消息?我试过在这里和那里放置关闭语句,但它要么不起作用,要么我对尝试关闭已经关闭的频道感到恐慌......package mainimport (    "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 startbar startfoo startfoo startbar received 6bar received 2bar received 4bar received 12bar received 8bar received 10在我的实际程序中,每个“foo”函数都在进行过滤和一堆繁重的字符串正则表达式。而且我需要“bar”功能,因为它具有根据时间戳重新排序和序列化打印的工作,因此输出不会交错。
查看完整描述

2 回答

?
慕哥9229398

TA贡献1877条经验 获得超6个赞

你的程序在所有 goroutine 完成之前就退出了。在从 返回之前,您需要等待 thefoo和bargoroutines 完成main。


执行此操作的通常方法是使用 a 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 %d\n", stuff)

    }


    fmt.Println("bar end")

}


查看完整回答
反对 回复 2021-11-01
?
慕的地8271018

TA贡献1796条经验 获得超4个赞

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 %d\n", stuff)

        cmp <- true

    }


    fmt.Println("bar end")


    //verify that cmp can be closed (all output is done, and d is closed)

    cmp <- true

}


查看完整回答
反对 回复 2021-11-01
  • 2 回答
  • 0 关注
  • 167 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信