Golang 几种使用 Channel 的错误姿势

Go 的 goroutine 能够让繁琐的并发变得简单易用。Go 不能没有 channel 就像西方不能失去耶路撒冷。Channel 非常神奇,即使是经验丰富的工程师也会被它绊倒。下面让我们来谈谈开发人员在使用 Go 中的 Channel 时常犯的一些错误,以及如何避免这些错误。

Deadlocks

死锁是使用 channel 时可能遇到的最频繁的问题。当一个程序在等待永远不会发生的事情时被卡住,就会出现死锁。想象一下,你试图将数据发送到一个 channel,但另一边却没有人接收数据。你的程序就这样停在那里,什么也不做。
来看看这段代码:

1
2
3
4
5
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}

该程序之所以挂起的原因是,代码试图向一个未缓冲通道发送数值,而没有任何 goroutine 可以接收该数值。解决方法很简单:使用 goroutine 发送值。下面是解决方法:

1
2
3
4
5
6
7
8
9
func main() {
ch := make(chan int)

go func() {
ch <- 1
}()

fmt.Println(<-ch)
}

通过生成一个 goroutine 来处理发送,就能确保主 goroutine 可以接收数据,程序就不会卡住。

Buffered Channels: 不要滥用缓冲区

当你想发送多个值而又不想立即阻塞时,缓冲通道是个不错的选择,但你需要小心使用。缓冲通道就像是数据的等待室。

举个通俗的例子,假设你正在经营一家小邮局。等候区只有一把椅子。这就像一个容量为 1 的 buffer channel。现在,如果有两个人来邮寄包裹,会发生什么情况呢?

第一个人坐下,没问题。但当第二个人到达时,他们就只能站在外面了,因为没有更多的空间。这正是本代码示例中发生的情况:

1
2
3
4
5
6
7
8
func main() {
ch := make(chan int, 1)

ch <- 1
ch <- 2 // This will block because the buffer is full

fmt.Println(<-ch)
}

我们的候车室只能容纳一个 “人”(在这里是一个号码),否则就会堵塞。

但如果我们把候车室变得更大呢?

1
2
3
4
5
6
7
8
9
func main() {
ch := make(chan int, 2)

ch <- 1
ch <- 2

fmt.Println(<-ch)
fmt.Println(<-ch)
}

现在,我们的候车室里有了两把椅子!两位 “顾客 “都能坐得舒服了,我们的小邮局也能顺利运转了。

所以当我们使用 buffer channel 的时候,要确保缓冲区足够大,否则可能会导致死锁。

Closing Channels: 不要忘记关闭

另一个高频的的错误是在使用完 channel 后忘记关闭。如果不关闭 channel,等待从 channel 接收数据的程序可能会一直等待并且永远不会到来的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
ch := make(chan int)

go func() {
for i := 0; i < 5; i++ {
ch <- i
}
}()

for i := range ch {
fmt.Println(i)
}
}

这段代码将打印 0 到 4 的数字,但随后会无限期挂起,因为 range 循环在等待更多数据。channel 从未关闭,因此循环不知道何时停止。

解决方法是什么?发送完数据后关闭通道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
ch := make(chan int)

go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()

for i := range ch {
fmt.Println(i)
}
}

现在,当循环收到通道关闭信号时,它就知道要停止了。


-------------The End-------------

本文标题:Golang 几种使用 Channel 的错误姿势

文章作者:cloud sjhan

发布时间:2024年08月26日 - 11:08

最后更新:2024年08月26日 - 11:08

原始链接:https://cloudsjhan.github.io/2024/08/26/Golang-几种使用-Channel-的错误姿势/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

cloud sjhan wechat
subscribe to my blog by scanning my public wechat account
坚持原创技术分享,您的支持将鼓励我继续创作!
0%
;