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 · 3921 次点击
    这是一个创建于 500 天前的主题,其中的信息可能已经有所发展或是发生改变。

    第一份代码如下

    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
    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   ·   实用小工具   ·   2494 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:21 · PVG 08:21 · LAX 16:21 · JFK 19:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.