Go中的锁(三) | 青训营笔记

70 阅读1分钟

相互等待 - WaitGroup

一个(组)协程需要等待另一组协程完成

Pasted image 20230521184025.png

type WaitGroup struct {
	noCopy noCopy // 特殊的结构体, 表示该结构体不能被拷贝  
	state1 [3]uint32 // counter, waiter counter, sema
}

Pasted image 20230521205001.png

Wait()

  • 如果被等待的协程没了, 直接返回
  • 否则, waiter + 1, 陷入sema

Done()

  • 被等待协程做完, 给counter - 1
  • 通过wg.Add(-1)实现

Add()

  • add counter
  • 被等待协程没做完, 或者没人在等待, 返回
  • 被等待协程都做完, 且有人等待, 唤醒所有sema中的协程

总结

  • WaitGroup:实现了一组协程等待另一组协程等待的协程
  • 陷入sema并记录个数
  • 被等待的协程计数归零时,唤醒所有sema中的协程

Once

整个程序运行过程中, 代码只执行一次 用来进行一些初始化的操作

// 使用
once := sync.Once{}
go once.Do(func(){})

// 实现
type Once struct {
	done uint32
	m Mutex
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}
  • 先判断是否已经改值
  • 没改, 尝试获取锁
  • 获取到锁的协程执行业务, 改值, 解锁
  • 冲突协程唤醒后直接返回

总结

  • sync.Once实现了一段代码只执行一次
  • 使用标志 + mutex:实现了并发冲突的优化

锁常见问题

锁拷贝

  • 锁拷贝可能导致锁的死锁问题
  • 使用vet工具可以检测锁拷贝问题
  • vet还能检测可能的bug或者可疑的构造

RACE竞争检测

  • 发现隐含的数据竞争问题
  • 可能是加锁的建议
  • 可能是bug的提醒

go-deadlock检测

  • 检测可能的死锁
  • 实际是检测获取锁的等待时间
  • 用来排查bug和性能问题