记录一个死锁异常--循环打印 AB go语言

使用两个 go 程循环打印 AB ,结果运行后发现死锁,"fatal error: all goroutines are asleep - deadlock!"。其中有两个原因:

1,触发第一次打印 A 的代码是 ch1 <- struct{}{},如果这行代码在主线程执行,那么在给阻塞在 ch1 的 go程唤醒的时候这个 go程可能还没有启动运行,所以 ch1 <- struct{}{} 最好要放在一个单独的 go程里运行。

2,当打印完最后一个 B 的时候不要再往 ch1 发送 struct{}{} 了,除非有程序接收。如果没有接收的话第一个 go程里的 for 循环已经结束了,第一个 go程已经结束运行,第二个go程被阻塞挂起,主线程也被阻塞,当前无任何活跃 goroutine,满足死锁条件,就会发生死锁报错。

解决方案:

1,打印 B 的 go程打印完最后一个 B 后不要再给 ch1 发送数据了,因为此时 go程1 已经结束运行,主线程也在阻塞中,此时的 ch1 没有接收方。这是优先方案。

2,go程1 在 for 循环结束后先不急着结束,还要接收一下 B 发送过来的最后一个 struct{}{},这可以让 go程2 不被阻塞。

Go 复制代码
func Test6(t *testing.T) {
	 
	ch1 := make(chan struct{})
	ch2 := make(chan struct{})

	 
	var wg sync.WaitGroup
	wg.Add(2)

	// 启动 goroutine 1:打印 A
	go func() {
		defer wg.Done()  
		// 打印 5 次(与 goroutine 2 交替,共 10 次输出)
		for i := 0; i < 5; i++ {
			<-ch1             // 等待获取执行权限(阻塞,直到收到数据)
			fmt.Print("A")    
			ch2 <- struct{}{} // 将执行权限传递给 goroutine 2
		}
        // 如果不写这行的话,那么 打印B的go程 打印完第5个B的时候就不要给ch1发送数据了
		<-ch1
        fmt.Println("go1 执行结束")

	}()

	// 启动 goroutine 2:打印 B
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			<-ch2 // 等待获取执行权限
			fmt.Print("B")
			//if i < 4 {
			ch1 <- struct{}{}
			//} // 将执行权限传递给 goroutine 1
		}
        fmt.Println("go2 执行结束")
	}()

	  // 触发第一个 goroutine 执行 ,避免主goroutine阻塞 
	go func() {
		ch1 <- struct{}{}
	}()

	wg.Wait()

	// 关闭通道 ,防止内存泄漏 
	close(ch1)
	close(ch2)

	fmt.Println("\n交替打印完成")
}
相关推荐
Leo2821 分钟前
LangGraph:把 Agent 从“会回答”推进到“可控执行”的工程架构
后端
ACP广源盛1392462567320 分钟前
GSV6155@ACP# Type-C 视频信号转换芯片:技术架构、工程痛点适配与落地场景分析
大数据·c语言·开发语言·人工智能·分布式·嵌入式硬件·架构
铁皮饭盒24 分钟前
网页端OCR, 加载6mb大小模型, 又快又准, 百度这次真香
前端·后端
程序员-Benothing27 分钟前
Java集合:List实现类深度对比
java·开发语言·后端·面试·list
酷在前行35 分钟前
【R论文复刻】Nature Communications 冲积图进阶:状态转移、流带排序与多面板排版(保姆级教程)
开发语言·r语言
减瓦1 小时前
用 Git 为本地文件打造一台时光机
开发语言·git·编辑器
云浪1 小时前
给 AI Agent 加上语音交互:ASR + 流式 TTS 实战
前端·人工智能·后端
卷无止境1 小时前
当代码要上线前,谁在替你把关?——Python安全审查的方法论与实践
后端·python
冻柠檬飞冰走茶1 小时前
PTA基础编程题目集 7-37 整数分解为若干项之和(C语言实现)
c语言·开发语言·数据结构·算法
卷无止境1 小时前
Python Dataclasses:让类定义回归简洁的艺术
后端·python