Golang中为什么selunlock会按照与sellock相反的顺序进行解锁?

Golang中为什么selunlock会按照与sellock相反的顺序进行解锁? 为什么 selunlock 以与 sellock 相反的顺序解锁?

func sellock(scases []scase, lockorder []uint16) {
	var c *hchan
	for _, o := range lockorder {
		c0 := scases[o].c
		if c0 != nil && c0 != c {
			c = c0
			lock(&c.lock)
		}
	}
}
func selunlock(scases []scase, lockorder []uint16) {
	for i := len(scases) - 1; i >= 0; i-- {
		c := scases[lockorder[i]].c
		if c == nil {
			break
		}
		if i > 0 && c == scases[lockorder[i-1]].c {
			continue // will unlock it on the next iteration
		}
		unlock(&c.lock)
	}
}

更多关于Golang中为什么selunlock会按照与sellock相反的顺序进行解锁?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中为什么selunlock会按照与sellock相反的顺序进行解锁?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言的select实现中,selunlock按照与sellock相反的顺序解锁是为了避免死锁。这是经典的锁获取顺序问题:当多个goroutine同时操作多个channel时,必须保证所有goroutine都以相同的顺序获取锁,否则可能产生死锁。

问题场景示例

假设有两个channel:ch1和ch2,两个goroutine同时执行select:

// Goroutine 1
select {
case <-ch1:
case <-ch2:
}

// Goroutine 2  
select {
case <-ch2:
case <-ch1:
}

死锁风险分析

如果sellock按照case出现的顺序加锁:

  • Goroutine 1:先锁ch1,再锁ch2
  • Goroutine 2:先锁ch2,再锁ch1

这就形成了循环等待

  1. Goroutine 1持有ch1锁,等待ch2锁
  2. Goroutine 2持有ch2锁,等待ch1锁
  3. 死锁发生

Go的解决方案

// sellock:按照lockorder排序后的顺序加锁
func sellock(scases []scase, lockorder []uint16) {
	var c *hchan
	for _, o := range lockorder {  // 正序遍历
		c0 := scases[o].c
		if c0 != nil && c0 != c {
			c = c0
			lock(&c.lock)  // 按排序顺序加锁
		}
	}
}

// selunlock:按照相反顺序解锁
func selunlock(scases []scase, lockorder []uint16) {
	for i := len(scases) - 1; i >= 0; i-- {  // 逆序遍历
		c := scases[lockorder[i]].c
		if c == nil {
			break
		}
		if i > 0 && c == scases[lockorder[i-1]].c {
			continue // 同一个channel只解锁一次
		}
		unlock(&c.lock)  // 按相反顺序解锁
	}
}

关键机制

  1. 统一排序lockorder数组确保所有goroutine对channel的锁获取顺序一致
  2. 相反顺序解锁:这是锁管理的最佳实践,符合LIFO(后进先出)原则
  3. 去重处理:通过c == scases[lockorder[i-1]].c检查避免重复解锁同一channel

实际执行流程

对于select中的多个channel case,假设排序后顺序为[ch2, ch1, ch3]:

加锁顺序:ch2 → ch1 → ch3
解锁顺序:ch3 → ch1 → ch2

这种相反顺序的解锁确保了:

  • 所有goroutine以相同顺序获取锁(避免死锁)
  • 解锁顺序与加锁顺序相反(符合锁管理规范)
  • 即使goroutine在select中途返回,也能正确释放所有已获取的锁

这种设计是操作系统和并发编程中标准的锁顺序管理策略在Go runtime中的具体实现。

回到顶部