在多进程或者多协程场景中,如果想要保证代码只运行一次,防止出现并发问题,大多数的办法是通过加锁的形式,但是go语言有自带的函数: once.Do 进行解决
假设有个需求,变量num原始数值为1,是想把变量num进行一次累加操作,想要得到num的数值为2
样例
func TestCurren(t *testing.T) {
num := 1
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
num++ // 假设具体的业务逻辑
wg.Done()
}(num)
}
wg.Wait()
t.Log("输出数据", num)
}
代码执行结果
=== RUN TestCurren
once_test.go:48: 输出数据 6
--- PASS: TestCurren (0.00s)
PASS
以上的逻辑,是分配了5个协程,但是在5个协程中,分别作了一次累加操作,所以最后的输出值是6,并非是2
使用once.Do进行改造
func TestCurren1(t *testing.T) {
num := 1
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
once.Do(func() {
num++
})
wg.Done()
}(num)
}
wg.Wait()
t.Log("输出数据", num)
}
代码执行结果
=== RUN TestCurren1
once_test.go:64: 输出数据 2
--- PASS: TestCurren1 (0.00s)
PASS
在once.Do里,虽然5个协程都执行了,但是只会执行一次具体的逻辑,所以输出的结果是2。