跳到主要内容

数据安全与锁

4.1、互斥锁

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。

package main

import (
"fmt"
"sync"
)

var wg sync.WaitGroup
var lock sync.Mutex
var x = 0

func add() {
//lock.Lock()
x++
//lock.Unlock()
wg.Done()
}

func main() {
wg.Add(1000)
for i:=0;i<1000 ;i++ {
go add()
}
wg.Wait()
fmt.Println(x)
}

使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。

4.2、读写锁

在读多写少的环境中,可以优先使用读写互斥锁(sync.RWMutex),它比互斥锁更加高效。sync 包中的 RWMutex 提供了读写互斥锁的封装。

package main

import (
"fmt"
"sync"
"time"
)

// 效率对比

// 声明读写锁
var rwlock sync.RWMutex
var mutex sync.Mutex
var wg sync.WaitGroup
// 全局变量
var x int

// 写数据
func write() {
//mutex.Lock()
rwlock.Lock()
x += 1
fmt.Println("x",x)
time.Sleep(10 * time.Millisecond)
//mutex.Unlock()
rwlock.Unlock()
wg.Done()
}

func read(i int) {

//mutex.Lock()
rwlock.RLock()
time.Sleep(time.Millisecond)
fmt.Println(x)
//mutex.Unlock()
rwlock.RUnlock()
wg.Done()
}

// 互斥锁执行时间:18533117400
// 读写锁执行时间:1312065700
func main() {
start := time.Now()
wg.Add(1)
go write()

for i := 0; i < 1000; i++ {
wg.Add(1)
go read(i)
}

wg.Wait()

fmt.Println("运行时间:", time.Now().Sub(start))
}

4.3、map锁

Go语言中内置的map不是并发安全的.

并发读是安全的

package main

import (
"fmt"
"sync"
)

func main() {
wg := sync.WaitGroup{}
m := make(map[int]int)
// 添一些假数据
for i := 0; i < 5; i++ {
m[i] = i*i
}
// 遍历打印
for i := 0; i < 5; i++ {
wg.Add(1)
go func(x int) {
fmt.Println(m[x], "\t")
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(m)
}

并发写则不安全

package main

import (
"fmt"
"sync"
)

func main() {
wg := sync.WaitGroup{}
//m := make(map[int]int)
var m = sync.Map{}

// 并发写
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i,i*i)
}(i)
}
wg.Wait()
fmt.Println(m.Load(1))
fmt.Println(m.Load(2))
}

4.4、原子性操作

  • 加锁操作比较耗时,整数可以使用原子操作保证线程安全

  • 原子操作在用户态就可以完成,因此性能比互斥锁高

提示

AddXxx():加减操作

提示

CompareAndSwapXxx():比较并交换

提示

LoadXxx():读取操作

提示

StoreXxx():写入操作

提示

SwapXxx():交换操作

原子操作与互斥锁性能对比:

package main

import (
"fmt"
"sync"
"sync/atomic"
"time"
)

// 效率对比
// 原子操作需要接收int32或int64
var x int32
var wg sync.WaitGroup
var mutex sync.Mutex

// 互斥锁操作
func add1() {
for i := 0; i < 500; i++ {
mutex.Lock()
x += 1
mutex.Unlock()
}
wg.Done()
}

// 原子操作
func add2() {
for i := 0; i < 500; i++ {
atomic.AddInt32(&x, 1)
}
wg.Done()
}

func main() {
start := time.Now()
for i := 0; i < 10000; i++ {
wg.Add(1)
go add1()
//go add2()
}
wg.Wait()
fmt.Println("x:", x)
fmt.Println("执行时间:", time.Now().Sub(start))
}