这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战
访问临界资源面临的问题
// 定义全局变量
var (
x = 0
wg sync.WaitGroup
)
// 访问临界资源
func f1() {
for i := 0; i < 10000; i++ {
x ++
}
wg.Done()
}
func main() {
wg.Add(2)
// 开启两个goroutine去访问临界资源
go f1()
go f1()
wg.Wait()
// 我们预期的结果应该为: 20000, 可是实际上结果并不是一个确定的数
fmt.Println("x的值为: ", x)
}
sync.Mutex
该锁为互斥锁
当一个goroutine获取到互斥锁, 可以访问临界区时, 其它goroutine等待 当前访问临界区的goroutine解开互斥锁时, 其它goroutine才可重新获取互斥锁
// 定义全局变量
var (
x = 0
wg sync.WaitGroup
// 定义一个全局互斥锁
lock sync.Mutex
)
// 访问临界资源
func f1() {
for i := 0; i < 10000; i++ {
// 在访问资源的时候加锁
lock.Lock()
x ++
// 访问资源完之后解锁
lock.Unlock()
}
wg.Done()
}
func main() {
wg.Add(2)
// 开启两个goroutine去访问临界资源
go f1()
go f1()
wg.Wait()
// 现在输出的结果就是我们预期的结果了
fmt.Println("x的值为: ", x)
}
sync.RWMutex
读锁定状态下: 可以再次加读锁定, 不能进行写锁定
写锁定状态下: 不能再次进行读锁定和写锁定
// 定义全局变量
var (
x = 0
wg sync.WaitGroup
// 互斥锁
//lock sync.Mutex
// 读写锁
rwLock sync.RWMutex
)
// 读操作
func read() {
// 加读锁
rwLock.RLock()
time.Sleep(time.Millisecond * 10)
// 解读锁
rwLock.RUnlock()
wg.Done()
}
// 写操作
func write() {
// 加写锁
rwLock.Lock()
x++
// 解写锁
rwLock.Unlock()
wg.Done()
}
func main() {
now := time.Now()
for i := 0; i < 100; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 100; i++ {
wg.Add(1)
go read()
}
wg.Wait()
// 使用互斥锁几次花费时间是: 1.078879827s 1.058491323s 1.067613451s
// 使用读写锁几次花费时间是: 21.997394ms 22.495443ms 22.509435ms
/*
从运行结果来看, 它们之间的花费时间是有量级上的差距
因此: 我们要在合适的时间, 使用合适的锁, 来编写我们的代码
*/
fmt.Println(time.Now().Sub(now))
}
sync.Once
一个Once变量只会执行一个Do函数
也就是说每一个要执行的函数, 都需要一个新的Once变量去调用
var once1 sync.Once
var once2 sync.Once
func f1() {
fmt.Println("我是f1, 我被执行了")
}
func f2() {
fmt.Println("我是f2, 我被执行了")
}
func main() {
once1.Do(f1)
once2.Do(f2)
// 下面的两个函数不会被执行
once1.Do(f1)
once2.Do(f2)
}
sync.Once 源码分析
/*
Once是一个结构体类型
里面有两个变量:
done, 用来标识函数do函数是否被执行
m, 一个互斥锁
*/
type Once struct {
done uint32
m Mutex
}
/*
先判断done标志, 决定是否执行f函数
*/
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
/*
先加锁, 在函数调用完之后, 更改done标志位, 然后解锁
调用函数
*/
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.Map
var m sync.Map
// 存值
m.Store("shaosiming", 18)
m.Store("dasiming", 20)
fmt.Printf("%#v \n", m)
// 取值
v, ok := m.Load("shaosiming")
if ok {
fmt.Println(v)
}
// 取值
// 如果key存在, 返回对应的值和true, 不会修改原来的值
// 如果key不存在, 返回false和存入的值, 并将键值对存入map
v, ok = m.LoadOrStore("dasiming2", 22)
if ok {
fmt.Println(v)
} else {
fmt.Println(v)
fmt.Println("map中没有对应的key, 所以存入该键值")
v, ok = m.Load("dasiming2")
if ok {
fmt.Println(v)
}
}
// 删除数据
//m.Delete("dasiming3")
// 如果key存在, 则删除对应的键值, 返回true
// 如果key不存在, 删除失败, 返回false
v, ok = m.LoadAndDelete("dasiming3")
if ok {
fmt.Println("删除数据成功, value: ", v)
} else {
fmt.Println("删除数据失败, value: ", v)
}
// map的遍历
m.Range(func(key, value interface{}) bool {
fmt.Println("key: ", key, " value: ", value)
return true
})