V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
ifconfig
V2EX  ›  Go 编程语言

Go Channel 学习的一个疑问,请大佬指教

  •  
  •   ifconfig · 2020-05-17 11:08:50 +08:00 · 2773 次点击
    这是一个创建于 1644 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    func main() {
    	boolChannel()
    }
    
    func boolChannel()  {
    	ch1 := make(chan int)
    	ch2 := make(chan bool)
    
    	go func() {
    		for i:=0;i<5;i++ {
    			ch1 <- i
    		}
    		ch2 <- true	//标识位写入 true
    	}()
    
    	go func() {
    		for x:= range ch1{
    			fmt.Println(x)
    		}
    	}()
    
    	<-ch2 //阻塞标识位
    }
    
    

    为什么这个输出结果一会是 0,1,2,3,一会是 0,1,2,3,4

    按道理来说,我在主进程一直阻塞了,应该等 goroutine 全部打印完才对呀,百思不得其解

    第 1 条附言  ·  2020-05-17 12:23:26 +08:00
    谢谢各位大佬回答,已解决,是第二个 goroutine 未读完就关闭的问题

    因为在网上看到一些 channel 面试题,想不通过 waitGroup 看看能不能解决,顺便加深对知识点的理解。

    10 楼、12 楼都回答了我的疑问,感谢~

    结贴~
    16 条回复    2020-05-17 12:29:01 +08:00
    tottea
        1
    tottea  
       2020-05-17 11:16:17 +08:00
    应该是 fmt.Println(x)这里的耗时吧,ch1 <- 4 完了之后马上 ch2 <- true,主协程一直阻塞后马上执行<-ch2,这时候 fmt.Println(4)这里卡在主协程结束前后,所以会有不稳定的情况
    我的理解是这样
    beidounanxizi
        2
    beidounanxizi  
       2020-05-17 11:16:55 +08:00
    这不是很正常啊 打印的 goroutine 你有没有同步机制 去保证 先于 main goroutine 结束 ?

    去看看 GMP 的调度把
    smallyu
        3
    smallyu  
       2020-05-17 11:19:11 +08:00
    ch2 <- true 上面加一行 time.Sleep(1 * time.Second)
    thet
        4
    thet  
       2020-05-17 11:20:51 +08:00 via iPhone
    <-ch2 接受到值,就往后执行了,这时候第二个 goroutine 可能还没执行完成
    ysmood
        5
    ysmood  
       2020-05-17 11:23:02 +08:00
    删掉 ch2 <- true 就可以了
    wangsongyan
        6
    wangsongyan  
       2020-05-17 11:23:44 +08:00 via iPhone
    打印完后再往 ch2 写值
    cabing
        7
    cabing  
       2020-05-17 11:31:01 +08:00
    加入 wait group 或者 context,不能保证你的 print 在 main 之前
    CEBBCAT
        8
    CEBBCAT  
       2020-05-17 11:36:51 +08:00
    这两个 go 出去的 func 并没有形成锁,只有第一个 func 和 boolChannel 形成了锁,所以你能确保的是全部写入后才会终结 boolChannel 的执行,但 fmt.Println(x)一定执行吗?不一定,因为可能正准备 fmt.Println(x),这个 goroutine 就被切到等待队列里了

    (我不知道应不应该使用锁这个名词,基础忘掉了大半,欢迎拍砖)
    gamexg
        9
    gamexg  
       2020-05-17 11:44:19 +08:00 via Android
    第一个 go 协程结束时关闭信道
    第二个协程直接在主线程执行。

    ch2 可以去掉。
    reus
        10
    reus  
       2020-05-17 12:10:58 +08:00   ❤️ 1
    ch2 <- true 改成 close(ch1)

    ch2 <- true 移到第二个 for 后面
    ifconfig
        11
    ifconfig  
    OP
       2020-05-17 12:19:14 +08:00
    @reus 大佬牛逼
    damingxing
        12
    damingxing  
       2020-05-17 12:19:44 +08:00
    4 进入 ch1 后,true 进入 ch2,
    情况 1:主程序先得到 ch2,第二个 func 还没来得及得到 ch1 或者没来得及打印, 程序就退出 输出 0,1,2,3
    情况 2:第二个 func 得到 ch1 并且打印,主程序还没来得及得到 ch2,输出 0,1,2,3,4
    damingxing
        13
    damingxing  
       2020-05-17 12:23:39 +08:00
    ch2 <- true //标识位写入 true
    这句放到第二个 func 后面就行了
    ifconfig
        14
    ifconfig  
    OP
       2020-05-17 12:25:16 +08:00
    @damingxing 通俗易懂理解了,其实就是想看看不通过 waitGroup 能不能解决,多谢~
    damingxing
        15
    damingxing  
       2020-05-17 12:28:05 +08:00
    @ifconfig 可以的,按照云风大侠的说法,通道是最佳解决方案
    useben
        16
    useben  
       2020-05-17 12:29:01 +08:00
    一般是发送 goroutine 是 close 发送的 chan,接收 goroutine 是关闭阻塞 chan
    这样的好处是不会造成 goroutine 泄露,发送完 close,接收 for chan 还是可以接收完数据的,会自己退出,这时候 close 或者发送信号到退出 chan 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2654 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:59 · PVG 12:59 · LAX 20:59 · JFK 23:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.