两份 Go 代码的对比,为什么第一份代码不会 panic,第二份代码会 panic - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
CarrieBauch
V2EX    Go 编程语言

两份 Go 代码的对比,为什么第一份代码不会 panic,第二份代码会 panic

  •  
  •   CarrieBauch 2023-07-06 08:39:31 +08:00 4563 次点击

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

    第一份代码如下

    package main import ( "fmt" "time" ) func main() { var ball = make(chan string) kickBall := func(playerName string) { for { fmt.Print(<-ball, "传球", "\n") time.Sleep(1 * time.Second) ball <- playerName } } go kickBall("张三") go kickBall("李四") go kickBall("王五") go kickBall("赵六") ball <- "裁判" var c chan bool <-c } 

    分界线


    第二份代码如下

    package main func main() { var c chan bool <-c } 
    第 1 条附言    2023-07-06 10:10:15 +08:00
    结贴了

    根本原因:所有 goroutine 都睡了就会 panic

    第一份代码是因为 go 启动的协程,还在不断的运行,所以不会 panic
    第二份代码只剩下孤零零的一个 <-c ,这样会导致仅有的一个主协程睡眠,所以会 panic
    21 条回复    2023-07-06 18:16:57 +08:00
    0o0O0o0O0o
        1
    0o0O0o0O0o  
       2023-07-06 08:44:51 +08:00 via iPhone
    第一份代码也是有 panic 的潜力的,建议读 https://tour.go-zh.org/concurrency/2
    aarontian
        2
    aarontian  
       2023-07-06 08:45:29 +08:00 via Android
    所有线程都阻塞就 panic 了
    rekulas
        3
    rekulas  
       2023-07-06 08:48:14 +08:00
    第二个没有运行中的协程,编译器直接判断死锁了,随便加一个就可以了
    rekulas
        4
    rekulas  
       2023-07-06 08:49:45 +08:00
    说错 不是编译器判断 是运行中判断的
    Rooger
        5
    Rooger  
       2023-07-06 09:01:27 +08:00
    能写出第二个代码,有一个基本的原则你没有弄明白。即 channel 的用途:在不同的协程之间进行消耗的传递,而第二份代码,即对 channel 没有初始化,也没有额外的协程存在。
    0o0O0o0O0o
        6
    0o0O0o0O0o  
       2023-07-06 09:17:17 +08:00
    @0o0O0o0O0o #1 emmm 抱歉看错了
    Seanfuck
        7
    Seanfuck  
       2023-07-06 09:24:10 +08:00
    chan 不是要 make 来创建吗,var **也行?
    8355
        8
    8355  
       2023-07-06 09:29:40 +08:00
    第二个没懂先看基础文档吧。。。。。
    paceewang1
        9
    paceewang1  
       2023-07-06 09:48:52 +08:00
    @Seanfuck var 是声明
    hsfzxjy
        10
    hsfzxjy  
       2023-07-06 09:59:00 +08:00 via Android
    所有 goroutine 都睡了就会 panic
    CarrieBauch
        11
    CarrieBauch  
    OP
       2023-07-06 10:01:56 +08:00
    @aarontian
    明白了,panic 会发生在所有的协程都被阻塞的情况下。把第一个程序简化为这样之后,也不会 panic 。因为 go 启动的一个协程在不断的运行,所以不会 panic

    package main

    func main() {
    go func() {
    for {
    }
    }()
    var c chan bool
    <-c
    }
    CarrieBauch
        12
    CarrieBauch  
    OP
       2023-07-06 10:02:28 +08:00
    @rekulas
    明白了,多谢多谢
    CarrieBauch
        13
    CarrieBauch  
    OP
       2023-07-06 10:03:20 +08:00
    @hsfzxjy 感谢感谢
    Richard14
        14
    Richard14  
       2023-07-06 10:15:56 +08:00
    我看 op 的贴,把第二段代码加了个放东西的协程,感觉就不会出错了,然而还是报错,OP 知道啥原因吗?
    package main

    import (
    "fmt"
    )

    func main() {
    var c chan bool
    go func() {
    c <- true
    }()
    fmt.Println(<-c)
    }
    hsfzxjy
        15
    hsfzxjy  
       2023-07-06 10:20:09 +08:00 via Android   1
    @Richard14 对 nil chan 做 send 或 recv 都会导致永远阻塞
    CarrieBauch
        16
    CarrieBauch  
    OP
       2023-07-06 10:39:00 +08:00   1
    @Richard14
    你把代码改成这样,就不会 panic 了

    原因就是 channel 没有初始化,只是做了 var 的声明

    package main

    import (
    "fmt"
    )

    func main() {
    //var c chan bool
    c := make(chan bool)
    go func() {
    c <- true
    }()
    fmt.Println(<-c)
    }
    lasuar
        17
    lasuar  
       2023-07-06 10:45:20 +08:00
    go 允许对一个 nil chan 进行读操作,这会导致阻塞。第二个例子会监测到死锁 panic 的原因是程序中没有其他 goroutine 正在运行。值得说明的是,这里不仅仅是要求有其他 goroutine 在运行,而且也不能全部同时阻塞,此时 go 运行时会监测到程序无法解除阻塞状态,从而再次导致死锁 panic 。

    下面的代码说明了 [其他 goroutine 不能全部同时阻塞的情况] :
    ```
    go func() {
    var cc = make(chan bool)
    <-cc
    }()
    var c = make(chan bool)
    <-c
    ```

    最后:通过第二段代码(无论 chan 是否通过 make 创建)来阻塞主程序不是合理的实践,原因上面说了,当程序中只剩下一个 goroutine (主)在运行时,这种代码会死锁 panic 并退出。
    crescentBLADE
        18
    crescentBLADE  
       2023-07-06 10:48:30 +08:00
    @Richard14 把 “var c chan bool” 改成 “var c = make(chan bool)”就可以了,未初始化的 chan 变量并没有分配任何内存空间,因此无法进行读写操作
    codehz
        19
    codehz  
       2023-07-06 13:40:50 +08:00
    import 一个"net"也不会 panic
    BinaryDH
        20
    /div> BinaryDH  
       2023-07-06 17:47:57 +08:00
    唉,问了一个问题,还得到了一份错误的答案!好好看看 channel 和 groutine 的文档吧,每一次阅读和编写 demo ,相信你都能有不同的提升。
    CarrieBauch
        21
    CarrieBauch  
    OP
       2023-07-06 18:16:57 +08:00
    @BinaryDH 求明说,或者给一个去验证的方向
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5122 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 09:09 PVG 17:09 LAX 01:09 JFK 04:09
    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