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

将通道从无缓冲更改为缓冲会阻止 goroutine 运行

将通道从无缓冲更改为缓冲会阻止 goroutine 运行

Go
动漫人物 2022-11-08 15:21:45
这是一个在 goroutine 中使用通道和选择的练习。如果断开连接通道更改为缓冲通道,则 goroutine 根本不会运行。为什么从无缓冲通道更改为缓冲通道会阻止运行 goroutine?func SelectDemo(wg *sync.WaitGroup) {    messageCh := make(chan int, 10)    disconnectCh := make(chan struct{})    //  go routine won't run if channel is buffered    //disconnectCh := make(chan struct{}, 1)    defer close(messageCh)    defer close(disconnectCh)    go func() {        fmt.Println("  goroutine")        wg.Add(1)        for {            select {            case v := <-messageCh:                fmt.Println(v)            case <-disconnectCh:                fmt.Println("  disconnectCh")                //  empty the buffered channel before exiting                for {                    select {                    case v := <-messageCh:                        fmt.Println(v)                    default:                        fmt.Println("  disconnection, return")                        wg.Done()                        return                    }                }            }        }    }()    fmt.Println("Sending ints")    for i := 0; i < 10; i++ {        messageCh <- i    }    fmt.Println("Sending done")    disconnectCh <- struct{}{}}这是从 main 调用函数的代码。我使用等待组来确保 goroutine 在程序退出之前完成:wg := sync.WaitGroup{}ch09.SelectDemo(&wg)wg.Wait()
查看完整描述

2 回答

?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

该代码逻辑有许多缺陷 - 其中一些是:

1- 由于messageCh被缓冲,此代码没有阻塞:


    for i := 0; i < 10; i++ {

        messageCh <- i

    }

所以下一个代码在快速运行路径中:


disconnectCh <- struct{}{}

如果您进行disconnectCh缓冲,则此行也不会阻塞运行,并且该SelectDemo函数可能会在运行wg.Add(1).


所以:你必须把:


wg.Add(1)


go func() {

2-即使使用wg.Add(1)之前的go func() { 代码-

您也有:


    defer close(messageCh)

    defer close(disconnectCh)

这将在函数返回时关闭两个通道SelectDemo这select是一个随机选择,因为两个通道都准备好了:


fmt.Println("  goroutine")

        for {

            select {

            case v := <-messageCh:

                fmt.Println(v)

            case <-disconnectCh:

第二个选择很可能是:


                for {

                    select {

                    case v := <-messageCh:

                        fmt.Println(v)

                    default:

                        fmt.Println("  disconnection, return")

                        wg.Done()

                        return

                    }

                }

将在messageCh关闭后0永远运行,在读取通道数据后永远返回:


case v := <-messageCh:

    fmt.Println(v)


查看完整回答
反对 回复 2022-11-08
?
犯罪嫌疑人X

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

程序执行速度快


访问网址:https ://pkg.go.dev/sync#WaitGroup.Add


请注意,当计数器为零时发生的具有正增量的调用必须在等待之前发生。具有负增量的调用或具有正增量的调用在计数器大于零时开始,可能随时发生。通常这意味着对 Add 的调用应该在创建 goroutine 的语句或其他要等待的事件之前执行。如果重用 WaitGroup 来等待多个独立的事件集,则必须在所有先前的 Wait 调用都返回后发生新的 Add 调用。请参阅 WaitGroup 示例。


func SelectDemo(wg *sync.WaitGroup) {


    messageCh := make(chan int, 10)

    disconnectCh := make(chan struct{}, 1)

    //  go routine won't run if channel is buffered

    //disconnectCh := make(chan struct{}, 1)


    wg.Add(1)


    defer close(messageCh)

    defer close(disconnectCh)

    go func() {

        fmt.Println("  goroutine")

        for {

            select {

            case v := <-messageCh:

                fmt.Println(v)

            case <-disconnectCh:

                fmt.Println("  disconnectCh")

                //  empty the buffered channel before exiting


                fmt.Println("  disconnection, return")

                wg.Done()

                return

            }

        }

    }()


    fmt.Println("Sending ints")

    for i := 0; i < 10; i++ {

        messageCh <- i

    }


    //Delay sending exit signal

    time.Sleep(3 * time.Second)


    fmt.Println("Sending done")

    disconnectCh <- struct{}{}

}


我修改了你的代码


再试一次!!!


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

添加回答

举报

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