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

2023-07-06 08:39:31 +08:00
 CarrieBauch

第一份代码如下

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
}

3971 次点击
所在节点    Go 编程语言
21 条回复
0o0O0o0O0o
2023-07-06 08:44:51 +08:00
第一份代码也是有 panic 的潜力的,建议读 https://tour.go-zh.org/concurrency/2
aarontian
2023-07-06 08:45:29 +08:00
所有线程都阻塞就 panic 了
rekulas
2023-07-06 08:48:14 +08:00
第二个没有运行中的协程,编译器直接判断死锁了,随便加一个就可以了
rekulas
2023-07-06 08:49:45 +08:00
说错 不是编译器判断 是运行中判断的
Rooger
2023-07-06 09:01:27 +08:00
能写出第二个代码,有一个基本的原则你没有弄明白。即 channel 的用途:在不同的协程之间进行消耗的传递,而第二份代码,即对 channel 没有初始化,也没有额外的协程存在。
0o0O0o0O0o
2023-07-06 09:17:17 +08:00
@0o0O0o0O0o #1 emmm 抱歉看错了
Seanfuck
2023-07-06 09:24:10 +08:00
chan 不是要 make 来创建吗,var **也行?
8355
2023-07-06 09:29:40 +08:00
第二个没懂先看基础文档吧。。。。。
paceewang1
2023-07-06 09:48:52 +08:00
@Seanfuck var 是声明
hsfzxjy
2023-07-06 09:59:00 +08:00
所有 goroutine 都睡了就会 panic
CarrieBauch
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
2023-07-06 10:02:28 +08:00
@rekulas
明白了,多谢多谢
CarrieBauch
2023-07-06 10:03:20 +08:00
@hsfzxjy 感谢感谢
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
2023-07-06 10:20:09 +08:00
@Richard14 对 nil chan 做 send 或 recv 都会导致永远阻塞
CarrieBauch
2023-07-06 10:39:00 +08:00
@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
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
2023-07-06 10:48:30 +08:00
@Richard14 把 “var c chan bool” 改成 “var c = make(chan bool)”就可以了,未初始化的 chan 变量并没有分配任何内存空间,因此无法进行读写操作
codehz
2023-07-06 13:40:50 +08:00
import 一个"net"也不会 panic🤣🤣🤣
BinaryDH
2023-07-06 17:47:57 +08:00
唉,问了一个问题,还得到了一份错误的答案!好好看看 channel 和 groutine 的文档吧,每一次阅读和编写 demo ,相信你都能有不同的提升。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/954452

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX