go无缓冲通道阻塞引发的问题

为啥:呜呜呜呜...是随机打印的, 而不是完整的十条?

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    onceBody := func() {
        fmt.Println(":Only once")
    }
    done := make(chan bool)
    for i := 0; i < 10; i++ {
        go func(i int) {
            once.Do(onceBody)
            done <- true
            fmt.Println("呜呜呜呜...")
        }(i)
    }
    for i := 0; i < 10; i++ {
        <-done
    }
}

结果:

:Only once
呜呜呜呜... // 这里可能是1-10条

而在"done <- true"前再加一个打印, 就可以输出10条?

阅读 3k
4 个回答

你的代码我把sync.Once拿掉也是你这样输出,所以问题不是sync.Once导致的

说明一下无缓冲通道阻塞的场景:

  1. 通道中无数据,但执行读通道。
  2. 通道中无数据,向通道写数据,但无协程读取。

再说说你的代码:

  1. 第1个for循环启用了10个协程,但是启动协程的时候没有阻塞处理(你的阻塞是在协程内部的),在阻塞之前输出日志会输出10次,这个正常,因为操作通道前不会阻塞,也就不会发生调度
  2. 第2个for循环读取了10次通道,这里是阻塞读取的,我打日志测试了。会输出10次读取成功
  3. 核心问题是阻塞调度问题,当你的fmt.Println("呜呜呜呜...")写在通道后面时,如果通道先阻塞,那代码下不去(也就是不走呜呜呜那行),没问题吧?
  4. 当第2个for读取完10次通道完成后,主协程无阻塞代码,所以主协程并不等待你第1个for循环里面的代码执行结果(也就是呜呜呜输出不可预测的原因),如果你想看到10个呜呜呜,那你可以在代码的最后加time.Sleep

另外协程的并发控制可以看看我的文章,写的很实用
golang限制协程数量

使用sync.WaitGroup 等待协程退出就可以啦。

这个和通道没有太大关系,而是main函数提前退出了,<-time.After(1*time.Second)或者 wg.Wait()

main本身就是一个主协程,你的循环里面起了很多goroutine,当一对一完成 就会结束主程了. 同步阻塞一下吧

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题