
本期分享:
1. select和for循环的死锁问题
2. defer改变函数返回值
3. 如何优雅地停止 goroutine
select 和 for 循环的死锁问题很多同学第一次写 Go 的并发代码时,都会尝试用 for + select 来监听 channel:
func main() {
ch := make(chan int)
for {
select {
case v := <-ch:
fmt.Println("receive:", v)
}
}
}这段代码有什么问题?
👉 如果 ch 永远没人写入,就会发生 死锁。因为 select 里没有 default 分支,case 又不满足,程序就会卡死。
方式一:加 default(非阻塞尝试):
for {
select {
case v := <-ch:
fmt.Println("receive:", v)
default:
// 没数据就先干点别的
time.Sleep(10 * time.Millisecond)
}
}方式二:关闭 channel 退出:
go func(ch chan int) {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}(ch)
for v := range ch {
fmt.Println("receive:", v)
}总结一句话:for+select 一定要考虑退出条件,否则就等着卡死吧。
defer 改变函数返回值defer 语句可能改变函数的返回值。defer 中的参数在声明时就被求值,而非函数执行时。先上代码:
func test1() int {
var i int = 1
defer func() {
i++
}()
return i
}结果是多少?
👉 答案是 1,而不是 2。
为什么?
因为 Go 的返回值在执行 return 时就已经确定了,defer 只是在 return 之后再执行。
那有没有办法让 defer 改变返回值呢?
有的,但必须用 命名返回值:
func test2() (i int) {
i = 1
defer func() {
i++
}()
return i
}结果就是 2。
所以记住口诀:
defer 无法改变最终结果。defer 可以操作并影响返回值。这就是 Go 的“延迟收尾”机制,一个小小的关键字,背后却有很多学问。
goroutine写过 Go 的人都知道:goroutine 可以轻松启动,但停止却不是那么容易。
比如下面的代码:
func worker() {
for {
fmt.Println("working...")
time.Sleep(time.Second)
}
}
func main() {
go worker()
time.Sleep(3 * time.Second)
fmt.Println("main exit")
}问题:main 退出了,worker 却还在那边跑,像个“孤魂野鬼”。
用 channel 作为退出信号
func worker(stop chan struct{}) {
for {
select {
case <-stop:
fmt.Println("worker stopped")
return
default:
fmt.Println("working...")
time.Sleep(time.Second)
}
}
}
func main() {
stop := make(chan struct{})
go worker(stop)
time.Sleep(3 * time.Second)
close(stop) // 发送退出信号
}用 context 控制(推荐,优雅且通用)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("worker stopped")
return
default:
fmt.Println("working...")
time.Sleep(time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(3 * time.Second)
cancel() // 结束 goroutine
}用标志位(不推荐,线程安全问题多,容易踩坑)
今天我们一起回顾了三个看似简单但容易掉坑的问题:
for+select 容易死锁,必须有退出条件。defer 对返回值的影响取决于是否使用命名返回值。goroutine 的退出需要优雅设计,推荐使用 context 来做统一控制。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。