Appearance
并发安全
- 并发安全指的是多个 goroutine 同时访问共享资源时,不会出现数据竞争、逻辑错误或不一致的结果。
互斥锁 (Mutex)
互斥锁,同一时间只允许一个 goroutine 访问资源(读 / 写互斥)。
lock1 会先执行,lock2 会阻塞,后续代码不会执行 因为 lock1 已获没有解锁
go
var mu sync.Mutex
var count int
func main() {
go lock1()
go lock2()
time.Sleep(1 * time.Second)
fmt.Println("count", count)
}
func lock1() {
mu.Lock()
count = 2
defer mu.Unlock()
fmt.Println("进入lock1")
fmt.Println("count1", count)
}
func lock2() {
// lock2 会阻塞,后续代码不会执行 因为 lock1 已获没有解锁
mu.Lock()
count = 3
fmt.Println("进入lock2")
fmt.Println("count2", count)
}- lock1,lock2 两个都会执行
go
var mu sync.Mutex
var count int
func main() {
go lock1()
go lock2()
time.Sleep(1 * time.Second)
fmt.Println("count", count)
}
func lock1() {
mu.Lock()
count = 2
defer mu.Unlock()
fmt.Println("进入lock1")
fmt.Println("count1", count)
}
func lock2() {
mu.Lock()
defer mu.Unlock()
count = 3
fmt.Println("进入lock2")
fmt.Println("count2", count)
}读写锁 (RWMutex)
写锁
- 写操作会阻塞所有读写。lock3会执行,lock4被阻塞不会执行。
go
// 读写锁
var rw sync.RWMutex
var count int
func main() {
go lock3()
go lock4()
time.Sleep(1 * time.Second)
fmt.Println("count", count)
}
func lock3() {
// 写锁
rw.Lock()
//defer rw.Unlock()
count = 3
fmt.Println("进入lock3")
fmt.Println("count3", count)
}
func lock4() {
// 写锁
rw.Lock()
//defer rw.Unlock()
count = 4
fmt.Println("进入lock4")
fmt.Println("count4", count)
}读锁
- 读操作不会阻塞其他读写操作。lock5,lock6会同时执行。
go
// 读写锁
var rw sync.RWMutex
var count int
func main() {
go lock5()
go lock6()
time.Sleep(1 * time.Second)
fmt.Println("count", count)
}
func lock5() {
// 读锁
rw.RLock()
//defer rw.Unlock()
count = 5
fmt.Println("进入lock5")
fmt.Println("count5", count)
}
func lock6() {
// 读锁
rw.RLock()
//defer rw.Unlock()
count = 6
fmt.Println("进入lock6")
fmt.Println("count6", count)
}原子操作
- 针对基本数据类型(如 int32、uint64 等)的简单操作,可使用原子函数避免锁的开销。
go
var counter int64
func add() {
atomic.AddInt64(&counter, 1) // 原子自增
}
func get() int64 {
return atomic.LoadInt64(&counter) // 原子读取
}通道
- 天然并发安全,无需显式锁。
go
var ch = make(chan int, 1) // 带缓冲的 channel 可减少阻塞
// 发送方
go func() {
ch <- 100 // 数据通过 channel 传递
}()
// 接收方
go func() {
val := <-ch // 安全获取数据
}()等待组
- 等待组,用于等待多个 goroutine 完成,防止主程序提前退出。
go
var wg sync.WaitGroup
func task() {
defer wg.Done() // 任务完成时减 1
// 业务逻辑
}
func main() {
for i := 0; i < 5; i++ {
wg.Add(1)
go task()
}
wg.Wait() // 等待所有任务完成
}sync.Map
- 并发安全的映射(哈希表),属于 sync 包,专门用于读多写少的并发场景。
- 提供了并发安全的 Store、Load、Delete、Range 等方法。
go
package main
import (
"fmt"
"sync"
)
var sMap sync.Map
func main() {
// 添加元素
sMap.Store("k1", "value1")
sMap.Store("k2", "value")
// 修改元素
sMap.Store("k1", "newValue")
// 更新元素
sMap.Swap("k1", "newValue2")
// 获取元素
v1, ok := sMap.Load("k1")
if ok {
fmt.Println(v1)
}
// 遍历
sMap.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true
})
// 删除元素
sMap.Delete("k2")
}sync.Once
- 确保某个操作只执行一次,常用于初始化操作。
- 示例:初始化数据库连接、加载配置文件、单例模式。
go
var once sync.Once
once.Do(func() {
// 初始化操作,只执行一次
})常见问题
- 死锁:多个 goroutine 等待对方释放锁,导致程序无法继续执行。
- 活锁:多个 goroutine 不断切换执行状态,导致程序无法继续执行。
- 饥饿:某个 goroutine 长期无法获取到锁,导致其他 goroutine 一直等待。
