go数据竞争与锁
给定一个x值,初始值为0,让其自加1000后,再自减1000,如未加锁,情况如下:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
fmt.Println("数据竞争与锁")
var x int
//自增1
for i:=0;i<=1000;i++ {
wg.Add(1)
go func() {
defer wg.Done()
x++
}()
}
//自减1
for i:=0;i<=1000;i++ {
wg.Add(1)
go func() {
defer wg.Done()
x--
}()
}
wg.Wait()
fmt.Println("计算后x值",x)
}
每次运行得到的结果自然不同:
[Running] go run "e:\vsgo\lock.go"
数据竞争与锁
计算后x值 31
[Done] exited with code=0 in 0.964 seconds
[Running] go run "e:\vsgo\lock.go"
数据竞争与锁
计算后x值 10
也就是说,产生了数据竞争,自增和自减函数均对x其进行了无序的操作,得不到想要值0,改进如下:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var lock sync.Mutex //互斥锁
func main() {
fmt.Println("数据竞争与锁")
var x int
//自增1
for i:=0;i<=1000;i++ {
wg.Add(1)
go func() {
defer wg.Done()
lock.Lock() //加锁
x++
lock.Unlock() //解锁
}()
}
//自减1
for i:=0;i<=1000;i++ {
wg.Add(1)
go func() {
defer wg.Done()
lock.Lock() //加锁
x--
lock.Unlock() //解锁
}()
}
wg.Wait()
fmt.Println("计算后x值",x)
}
得到理想值:
[Running] go run "e:\vsgo\lock.go"
数据竞争与锁
计算后x值 0
[Done] exited with code=0 in 1.12 seconds
[Running] go run "e:\vsgo\lock.go"
数据竞争与锁
计算后x值 0
然而实际项目中,互斥锁,锁的开销很大,对性能有影响,代价高。go语言中自带了一个原子性操作函数,atomic进行自增。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var wg sync.WaitGroup
func main() {
fmt.Println("数据竞争与锁")
var x int32
//自增1
for i:=0;i<1000;i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt32(&x,1)
}()
}
wg.Wait()
fmt.Println("计算后x值",x)
}
自增1结果:
[Done] exited with code=0 in 0.902 seconds
[Running] go run "e:\vsgo\lock.go"
数据竞争与锁
计算后x值 1000