1、互斥锁
互斥锁是一种常用的控制共享的资源访问的方法,它能够保证同时只有一个 goroutine 可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。
package main
import (
"fmt"
"sync"
)
func add() {
defer wg.Done()
//lock.Lock() // 加锁
x++
//lock.Unlock() // 解锁
}
var x = 0
var wg sync.WaitGroup
var lock sync.Mutex // 定义互斥锁
func main() {
wg.Add(100)
for i := 0; i < 100; i++ {
go add() // 启动100个并发
}
wg.Wait()
fmt.Println(x)
}
使用互斥锁能够保证同一时间有且只有一个 goroutine 进入临界区,其他的 goroutine 则在等候锁;当互斥锁释放后,等待的 goroutine 才可以获取锁进入临界区, 多个 goroutine 同时等待一个锁时,唤醒的策略是随机的。
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))
}
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))
}