Golang死锁vs操作系统死锁

目录

一、死锁

二、Golang死锁场景

[2.1 重复上锁](#2.1 重复上锁)

[2.2 不会减少的 WaitGroup](#2.2 不会减少的 WaitGroup)

[2.3 空select](#2.3 空select)

[2.4 channel](#2.4 channel)


一、死锁

1.golang中死锁的触发条件:

死锁是当 Goroutine 被阻塞而无法解除阻塞时产生的一种状态。

2.操作系统死锁:

发生死锁时,线程永远不能完成,系统资源被阻碍使用,以致于阻止了其他作业开始执行。在讨论处理死锁问题的各种方法之前,我们首先深入讨论一下死锁特点。

必要条件:

如果在一个系统中以下四个条件同时成立,那么就能引起死锁:

  • 互斥:至少有一个资源必须处于非共享模式,即一次只有一个线程可使用。如果另一线程申请该资源,那么申请线程应等到该资源释放为止。
  • 占有并等待:---个线程应占有至少一个资源,并等待另一个资源,而该资源为其他线程所占有。
  • 非抢占:资源不能被抢占,即资源只能被线程在完成任务后自愿释放。
  • 循环等待:有一组等待线程 {P0,P1,...,Pn},P0 等待的资源为 P1 占有,P1 等待的资源为 P2 占有,......,Pn-1 等待的资源为 Pn 占有,Pn 等待的资源为 P0 占有。

我们强调所有四个条件必须同时成立才会出现死锁。循环等待条件意味着占有并等待条件,这样四个条件并不完全独立。

二、Golang死锁场景

2.1 重复上锁

写写冲突,读写冲突,读读不冲突。golang中的锁是不可重入锁,对已经上了锁的写锁,再次申请锁是会报死锁。上了读锁的锁,再次申请写锁会报死锁,而申请读锁不会报错。

案例1:

重复上写锁

Go 复制代码
package main
import(
	"sync"
)
func main(){
	var lock sync.Mutex
	lock.Lock()
	lock.Lock()
}

结果:死锁

正常情况:

Go 复制代码
func main() {
	var lock sync.RWMutex
	lock.RLock()
	lock.RLock()
}
//正常执行

2.2 不会减少的 WaitGroup

不会减少的 WaitGroup会永久阻塞

案例1:

Go 复制代码
func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	wg.Wait()
  //报死锁错误
}

结果:

2.3 空select

案例:

Go 复制代码
package main

func main() {
	select {
	
	}
}
//报死锁错误

结果:

2.4 channel

1.为 nil 的channel 发送、接受数据都会阻塞;

2.无缓冲的channel 发送、接受数据都会阻塞。解决方案:边接受边读取

3.channel 缓冲区满了的,继续发送数据会阻塞。解决办法:读取channel中的数据

4.当 ch 中没有数据的时候,就是从空的channel中接受数据,for range ch 会发生阻塞,但是无法解除阻塞,发生死锁。 解决:当数据发送完了过后,close channel

案例1:

Go 复制代码
func main() {
	var ch chan struct{}
	ch <- struct{}{}
}
//报死锁错误

结果:

案例2:

Go 复制代码
func main() {
	ch := make(chan struct{})
	<- ch
}
//报死锁错误

结果:

参考:关于golang中死锁的思考与学习_Golang_脚本之家

相关推荐
彭岳林20 小时前
nil是什么?
go
浮尘笔记20 小时前
go-zero使用elasticsearch踩坑记:时间存储和展示问题
大数据·elasticsearch·golang·go
彭岳林20 小时前
err != nil ?
go
杰克逊的黑豹20 小时前
不再迷茫:Rust, Zig, Go 和 C
c++·rust·go
DemonAvenger2 天前
深入剖析 sync.Once:实现原理、应用场景与实战经验
分布式·架构·go
一个热爱生活的普通人3 天前
Go语言中 Mutex 的实现原理
后端·go
孔令飞3 天前
关于 LLMOPS 的一些粗浅思考
人工智能·云原生·go
小戴同学3 天前
实时系统降低延时的利器
后端·性能优化·go
Golang菜鸟4 天前
golang中的组合多态
后端·go
Serverless社区4 天前
函数计算支持热门 MCP Server 一键部署
go