
本文将通过一个实际案例,探讨 Go 语言并发编程中常见的死锁问题以及如何解决。
原案例中,程序将一个整数数组分割成两部分,分别交给两个 Goroutine 进行求和,并通过 channel 将结果传递回主 Goroutine。然而,由于 channel 未被正确关闭,导致主 Goroutine 在 range 循环中无限期等待,最终造成死锁。
解决死锁的关键在于确保 channel 在不再有数据发送时被关闭,或者采用其他方式来控制主 Goroutine 的退出。以下提供两种解决方案:
解决方案 1:使用计数器
此方案避免使用 close() 函数,而是通过计数器来控制主 Goroutine 的循环次数。
package main
import (
"fmt"
)
// Add adds the numbers in a and sends the result on res.
func Add(a []int, res chan<- int) {
sum := 0
for i := range a {
sum = sum + a[i]
}
res <- sum
}
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7}
n := len(a)
ch := make(chan int)
go Add(a[:n/2], ch)
go Add(a[n/2:], ch)
sum := 0
count := 0 // 初始化计数器
// 循环次数等于 Goroutine 的数量
for count < 2 {
s := <-ch
sum = sum + s
count++ // 每次接收到数据后递增计数器
}
fmt.Println(sum)
}代码解释:
酷纬企业网站管理系统Kuwebs是酷纬信息开发的为企业网站提供解决方案而开发的营销型网站系统。在线留言模块、常见问题模块、友情链接模块。前台采用DIV+CSS,遵循SEO标准。 1.支持中文、英文两种版本,后台可以在不同的环境下编辑中英文。 3.程序和界面分离,提供通用的PHP标准语法字段供前台调用,可以为不同的页面设置不同的风格。 5.支持google地图生成、自定义标题、自定义关键词、自定义描
- 初始化计数器: 在 main 函数中,我们初始化一个名为 count 的整数变量,用于记录从 channel 接收数据的次数。
- 循环条件: 使用 for count
- 递增计数器: 在每次从 channel 接收到数据后,递增 count 的值。
注意事项:
- 这种方法适用于 Goroutine 数量已知且固定的情况。
- 确保计数器的初始值和循环条件正确,否则可能导致程序无法正确结束或提前退出。
解决方案 2:在 Goroutine 中关闭 Channel
这种方案需要在 Add 函数中判断是否是最后一个 Goroutine 完成计算,并负责关闭 Channel。这需要引入额外的机制来同步 Goroutine 的状态,较为复杂,因此不推荐使用。
总结:
在 Go 并发编程中,合理管理 channel 的生命周期至关重要,尤其是在多个 Goroutine 之间进行数据传递时。死锁是常见的并发问题,通常是由于 channel 的阻塞导致。通过使用计数器或者在发送者明确知道不再有数据发送时关闭 channel,可以有效地避免死锁,保证程序的正确性和稳定性。在实际开发中,应根据具体场景选择合适的解决方案。计数器方式更简单直接,推荐优先使用。










