关于 golang channel 的问题,希望懂的大佬能进来看看 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
zyt19940914
V2EX    Go 编程语言

关于 golang channel 的问题,希望懂的大佬能进来看看

  •  
  •   zyt19940914 2019-08-28 15:00:06 +08:00 4259 次点击

    这是一个创建于 2244 天前的主题,其中的信息可能已经有所发展或是发生改变。

    关于 channel 在 golang 中国的官网上写着这样一句话:

    使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。

    同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。

    问题一: 如果是无缓冲的 channel,协程给一个信道发数据 但是没有其他协程接收数据,个人实测下来并没有出现死锁....

    问题二: 无缓冲的 channel 与有缓冲的 channel 发生死锁的场景区别,哪位能介绍下....?

    24 条回复    2019-08-29 09:35:55 +08:00
    choury
        1
    choury  
       2019-08-28 15:12:51 +08:00   1
    nybux
        2
    nybux  
       2019-08-28 15:14:31 +08:00
    package main

    func main() {
    ch := make(chan int)
    for {
    ch <- 1
    }
    }


    fatal error: all goroutines are asleep - deadlock!

    goroutine 1 [chan send]:
    main.main()
    a.go:7 +0x5c
    exit status 2
    zyt19940914
        3
    zyt19940914  
    OP
       2019-08-28 16:45:17 +08:00
    @nybux @choury 关于第二个问题 我自己实践发现,如果 channel 是有缓冲的,在 channel 被写满前,如果没有协程从通道内取,那不会产生死锁, 如果 channel 已经被写满了,这个时候再向里面写的时候就会报死锁。两位大佬认同我的总结吗......
    choury
        4
    choury  
       2019-08-28 17:13:03 +08:00
    @zyt19940914 是的,你可以把所有的 channel 都理解成有缓冲的,只是这个缓冲可能是 0,缓冲满了再写这个 goroutinues 就卡住,所有 goroutinues 都卡住就报死锁
    Gav1n
        5
    Gav1n  
       2019-08-28 17:37:00 +08:00
    楼主你好,问题 1 的代码能贴出来看看吗?
    Gav1n
       6
    Gav1n  
       2019-08-28 17:38:29 +08:00
    不好意思,我没看到协程,确实是这样的.等大佬来解释一下
    sujin190
        7
    sujin190  
       2019-08-28 17:57:15 +08:00
    可能你对死锁理解有点朴素了,这个的意思应该是,你往一个无缓冲的 channel 写入数据,这个写入过程必须等待有另外一个 goroutinue 来结收这个数据才能返回,如果这个时候再也没有任何一个另外 goroutinue 来结收数据了,那么这时候不是死锁了是啥?
    注意这个没有另外 goroutinue 来结收数据的意思,并不是当前没有另外一个 goroutinue 正在等待结收数据,而是当前和以后都不会有另外一个 goroutinue 来结收这个数据了,这种情况下应该是除了写入数据这个 goroutinue 以外所有 goroutinue 都已经执行结束,整个程序再也不会有另可运行的 goroutinue 来读取这个数据。

    对于有缓冲的 channel,则是写入数据当时如果没有另外 goroutinue 来读取这个数据,则放入缓冲中,自己可以返回来读取缓冲中的数据,所以这个并不会造成死锁。
    1608637229
        8
    1608637229  
       2019-08-28 18:02:11 +08:00
    借一下楼问一个简单的问题
    https://tour.go-zh.org/moretypes/11
    这里的第 10 行的 s = s[:0]
    s 被赋值为了 0 个为什么后面还能继续 s = s[:4]
    有点不能理解,按照我的理解难道不是 s 已经被赋值为空了吗
    sujin190
        9
    sujin190  
       2019-08-28 18:03:21 +08:00
    更简洁一点的说法应该是,我当前数据需要另外一个 goroutinue 来读取,而另一个可以读取这个数据 goroutinue 需要我来创建,所以这个和传统的死锁并没有什么区别。

    等待读取数据也是同理的。
    zichen
        10
    zichen  
       2019-08-28 18:03:56 +08:00
    这个不叫死锁应该叫阻塞吧……
    1608637229
        11
    1608637229  
       2019-08-28 18:08:09 +08:00
    @1608637229 难道是和 c 语言一样的指针吗,赋值 0 的时候依然还是数组的头地址没变。我不知道有没有 go 大佬解答一下
    sujin190
        12
    sujin190  
       2019-08-28 18:39:25 +08:00
    @1608637229 #8 这个和 c 语言完全不一样吧,不是元素访问,似乎 C 语言也没有这个操作的吧
    这个是对数组进行切片操作,空数组切片操作选择前四个不还是空数组么,内部应该有 min(4, len(s))之类的操作
    zyt19940914
        13
    zyt19940914  
    OP
       2019-08-28 18:39:56 +08:00
    sujin190
        14
    sujin190  
       2019-08-28 18:43:11 +08:00
    @zichen #10 阻塞的意思是现在将来一段时间等待其他事件,等待时间内不能做其他事情,但是等待肯定是可以有结果的,但是如果永远不会有结果返回,这个就叫死锁了啊
    zyt19940914
        15
    zyt19940914  
    OP
       2019-08-28 18:44:21 +08:00
    @zichen 运行的时候显示 fatal error: all goroutines are asleep - deadlock! 可能因为发生在主线程中.....? 所以报死锁 如果不是主线程,那是阻塞
    inhzus
        16
    inhzus  
       2019-08-28 18:48:18 +08:00
    @1608637229 #8 如果看过 stl 源码的话,golang slice 比较类似于 c++ vector,可以对比一下就理解了
    lilydjwg
        17
    lilydjwg  
       2019-08-28 18:49:42 +08:00
    @zichen #10 大概是因为 channel 的实现里有个锁,然后没有接收者的时候它就死了?

    Go 的 channel 不能在发送时知道有没有接收方吗(不管它在不在接收)? Rust 的 channel 如果接收方不在了是会返回错误的,Go 应该也能做到的吧?
    1608637229
        18
    1608637229  
       2019-08-28 18:55:18 +08:00
    @sujin190 看来下就是头指针,s = s[:0]头指针没有改变。改变的是 len (变为 0 )。因此后面只可以再赋值的时候 s = s[:4]只是把 len 改成了 4.
    lilydjwg
        19
    lilydjwg  
       2019-08-28 18:57:22 +08:00
    @1608637229 #11 不为什么。不需要理解。这是 Go 语言规范所规定的:

    > The capacity is a measure of that extent: it is the sum of the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by slicing a new one from the original slice.

    https://golang.google.cn/ref/spec#Slice_types

    @sujin190 #12 你点一下「 run 」就会发现这个和 Python 的切片不一样的。
    zarte
        20
    zarte  
       2019-08-28 19:01:12 +08:00
    问题一,这个有可能是你创建通道未初始化,然后传与取都是正常执行不过没数据。
    问题二:
    jobs := make(chan []string, len(newlist))
    go 协程一
    for{
    jobs <- addr
    }
    go 协程二
    func threadworker(id int, jobs <-chan []string) {
    for j := range jobs {
    liulaomo
        21
    liulaomo  
       2019-08-28 20:47:46 +08:00
    @zyt19940914

    > 使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。
    > 同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。

    这个需要需要上下文情景理解。 不过无论如何,这么描述都是欠妥的。这两句话想强调的是当所有用户协程都同时阻塞时,程序将崩溃退出(非 panic )。但这只是官方编译器的实现,白皮书对此并未做要求。

    @1608637229
    取子切片时,第二个下标可以大于基础切片的长度,但是不能大于基础切片的容量。具体见 @lilydjwg 列出的白皮书中的一段。
    janxin
        22
    janxin  
       2019-08-29 08:08:09 +08:00
    Go 的 runtime 是在调度过程中发现所有 goroutine 都处于等待状态时就才会发出 all goroutines are asleep - deadlock panic 的。这里比较容易迷惑人的是:不是出现了 panic 才有死锁,在实际应用中不出现 panic 的时候也有可能有死锁。
    janxin
        23
    janxin  
       2019-08-29 08:21:42 +08:00
    @lilydjwg 这个是调度器检查的,当所有 goroutine 都阻塞住了才 panic。
    poplar50
        24
    poplar50  
       2019-08-29 09:35:55 +08:00 via Android
    重点不是缓不缓冲,死锁是资源占用逻辑问题,如果一个协程需要从 channel 接收数据,主程发现除了该协程其他协程已经执行完毕了,那么就那个协程就永远接收不到数据,一直 block。
    同理 一个协程需要发送数据,但 channel 已满,这是它是 block 的状态,然后如果其他协程都已经执行完毕了,channel 仍然是慢的,此时这个协程如果不控制也会一直 block 下去,这个时候,也应该报死锁。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5296 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 01:20 PVG 09:20 LAX 18:20 JFK 21:20
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86