欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Golang笔记——常用库sync

Golang笔记——常用库sync

2025/2/22 2:07:31 来源:https://blog.csdn.net/haopingbiji/article/details/145201239  浏览:    关键词:Golang笔记——常用库sync

大家好,这里是Good Note,关注 公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Golang的常用库sync,提供了一系列工具来处理 并发编程 中的同步问题。

在这里插入图片描述

文章目录

    • sync
      • 1. `sync.Mutex` - 互斥锁
      • 2. `sync.RWMutex` - 读写锁
      • 3. `sync.WaitGroup` - 等待组
      • 4. `sync.Once` - 单次执行
      • 5. `sync.Cond` - 条件变量
      • 6. `sync.Pool` - 对象复用池
      • 7. sync.Map(并发安全的 Map)
      • 8. 原子操作(sync/atomic)
      • 9. 信号量(基于 channel 实现)
      • 10. 自旋锁(sync.Mutex 变体)
      • 总结
    • 历史文章
      • MySQL数据库
      • Redis
      • Golang

sync

sync 是 Go 标准库中的并发同步包,提供了一系列工具来处理 并发编程 中的同步问题。sync 包主要用于管理多个 goroutine 之间的共享资源访问,确保程序的正确性和线程安全。

1. sync.Mutex - 互斥锁

  • 作用:用于保护共享资源,保证同一时刻只有一个 goroutine 能访问临界区代码,避免竞态条件(Race Condition)。
  • 两个方法
    • Lock():获取锁,如果锁已经被其他 goroutine 获取,当前 goroutine 会阻塞。
    • Unlock():释放锁。

示例代码

package mainimport ("fmt""sync"
)var counter int
var mutex sync.Mutexfunc worker(wg *sync.WaitGroup) {defer wg.Done()for i := 0; i < 1000; i++ {mutex.Lock()   // 获取锁counter++      // 临界区:访问共享变量mutex.Unlock() // 释放锁}
}func main() {var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go worker(&wg)}wg.Wait()fmt.Println("Counter:", counter) // 输出:Counter: 5000
}

说明

  • 使用 sync.Mutex 锁定共享资源访问。
  • counter 在多个 goroutine 并发执行时是安全的,最终输出结果为 5000。

2. sync.RWMutex - 读写锁

  • 作用:区分读和写的操作:
    • 多个 goroutine 可以同时读取(并发读)。
    • 写操作会独占锁(互斥写),阻塞其他读和写操作。
  • 方法
    • RLock() / RUnlock():获取/释放读锁。
    • Lock() / Unlock():获取/释放写锁。

示例代码

package mainimport ("fmt""sync""time"
)var data int
var rwMutex sync.RWMutexfunc readData(id int, wg *sync.WaitGroup) {defer wg.Done()rwMutex.RLock()fmt.Printf("Reader %d: Read data %d\n", id, data)time.Sleep(time.Millisecond * 10)rwMutex.RUnlock()
}func writeData(id int, wg *sync.WaitGroup) {defer wg.Done()rwMutex.Lock()data += 1fmt.Printf("Writer %d: Wrote data %d\n", id, data)time.Sleep(time.Millisecond * 10)rwMutex.Unlock()
}func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1)go writeData(i, &wg) // 写操作}for i := 1; i <= 5; i++ {wg.Add(1)go readData(i, &wg) // 读操作}wg.Wait()
}

说明

  • 读写锁允许多个读操作同时进行,但写操作是独占的,其他读写操作会被阻塞。

3. sync.WaitGroup - 等待组

  • 作用:用于等待一组 goroutine 完成。
  • 方法
    • Add(n):增加等待的 goroutine 计数。
    • Done():完成一个 goroutine 的计数。
    • Wait():阻塞,直到计数为 0。

示例代码

package mainimport ("fmt""sync"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done()fmt.Printf("Worker %d is working\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 5; i++ {wg.Add(1)go worker(i, &wg)}wg.Wait()fmt.Println("All workers finished")
}

说明

  • wg.Add(1) 表示增加一个等待的 goroutine。
  • defer wg.Done() 表示 goroutine 完成时减少计数。
  • wg.Wait() 阻塞主线程,直到所有计数为 0。

4. sync.Once - 单次执行

  • 作用:确保某个操作(如初始化)只执行一次,常用的 close channel
  • 方法
    • Do(f func()):只会执行一次 f,无论有多少个 goroutine 调用。

示例代码

package mainimport ("fmt""sync"
)var once sync.Oncefunc initialize() {fmt.Println("Initializing...")
}func worker(id int, wg *sync.WaitGroup) {defer wg.Done()once.Do(initialize) // 确保 initialize 只执行一次fmt.Printf("Worker %d is working\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 5; i++ {wg.Add(1)go worker(i, &wg)}wg.Wait()
}

说明

  • sync.Once 确保 initialize 函数只执行一次,即使有多个 goroutine 调用它。

5. sync.Cond - 条件变量

  • 作用:在 goroutine 间通过条件变量进行信号通信。
  • 常用方法
    • Wait():等待条件满足。
    • Signal():唤醒一个等待的 goroutine。
    • Broadcast():唤醒所有等待的 goroutine。

示例代码

package mainimport ("fmt""sync""time"
)var ready bool
var cond = sync.NewCond(&sync.Mutex{})func worker(id int, wg *sync.WaitGroup) {defer wg.Done()cond.L.Lock()for !ready {cond.Wait() // 等待条件满足}cond.L.Unlock()fmt.Printf("Worker %d is working\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1)go worker(i, &wg)}time.Sleep(time.Second)cond.L.Lock()ready = truecond.Broadcast() // 唤醒所有等待的 goroutinecond.L.Unlock()wg.Wait()
}

说明

  • sync.Cond 用于等待条件满足。
  • Signal() 唤醒一个等待 goroutine,Broadcast() 唤醒所有等待的 goroutine。

6. sync.Pool - 对象复用池

  • 作用:用于缓存临时对象,减少内存分配次数,提升性能。
  • 常用方法
    • Get():获取对象。如果存储了多个对象,Get 获取哪个对象是 不确定的,没有特定的顺序。
    • Put():放回对象。

示例代码

package mainimport ("fmt""sync"
)var pool = sync.Pool{New: func() interface{} {return "new object"},
}func main() {obj := pool.Get()fmt.Println(obj) // 输出:new objectpool.Put("reused object")fmt.Println(pool.Get()) // 输出:reused object
}

说明

  • sync.Pool 可以复用对象,减少垃圾回收压力。
  • 如果池中没有对象,会调用 New 创建新对象 new object

7. sync.Map(并发安全的 Map)

作用:

  • sync.Map 是 Go 1.9 引入的并发安全的 Map,用于多协程环境下读写共享数据。
  • 相比普通的 mapsync.Map 内部实现了同步机制,避免了竞态条件。

特点:

  • 性能优化:适用于读取多、写入少的场景。
  • 并发安全:支持多协程同时读写。
  • 与普通 map 区别:不需要显式加锁,sync.Map 提供了专门的方法。

方法:

  • Store(key, value):存储键值对。
  • Load(key):获取对应的值。
  • Delete(key):删除键值对。
  • Range(f func(key, value)):遍历 Map 中的所有键值对。
  • LoadOrStore(key, value):如果键已存在,返回对应的值;不存在则存储新值。

示例代码:

package mainimport ("fmt""sync"
)func main() {var m sync.Map// 存储值m.Store("key1", "value1")m.Store("key2", "value2")// 加载值if val, ok := m.Load("key1"); ok {fmt.Println("key1:", val) // 输出: key1: value1}// 遍历所有键值对m.Range(func(key, value interface{}) bool {fmt.Println(key, ":", value)return true})// 删除键m.Delete("key1")if _, ok := m.Load("key1"); !ok {fmt.Println("key1 已删除")}
}

使用场景:

  • 并发访问共享的键值数据,且读多写少。
  • 在高并发情况下替代标准 map 避免手动加锁。

8. 原子操作(sync/atomic)

作用:

  • sync/atomic 提供了一组底层的原子操作,用于对整数、指针和其他变量进行原子级别的读写操作。
  • 原子操作是并发安全的,避免了竞态条件,不需要加锁。

常用方法:

  • AddInt32 / AddInt64:对整数执行加法操作。
  • LoadInt32 / LoadInt64:读取整数的值。
  • StoreInt32 / StoreInt64:设置整数的值。
  • CompareAndSwapInt32:比较并交换值,CAS 操作。
  • SwapInt32 / SwapInt64:交换值。

示例代码:

package mainimport ("fmt""sync""sync/atomic"
)func main() {var counter int32 = 0var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {defer wg.Done()for j := 0; j < 1000; j++ {atomic.AddInt32(&counter, 1) // 原子加 1}}()}wg.Wait()fmt.Println("Counter:", counter) // 输出: Counter: 10000
}

使用场景:

  • 在高并发场景下实现简单的计数器或标志位。
  • 用于替代互斥锁(sync.Mutex)以提升性能。

9. 信号量(基于 channel 实现)

Go 并没有内置的信号量类型,但可以通过 channel 实现类似信号量的机制,限制并发协程的数量。

示例代码:

package mainimport ("fmt""sync""time"
)func worker(id int, sem chan struct{}, wg *sync.WaitGroup) {defer wg.Done()// 获取信号量sem <- struct{}{}fmt.Printf("Worker %d is working...\n", id)time.Sleep(time.Second)// 释放信号量<-sem
}func main() {var wg sync.WaitGroupsem := make(chan struct{}, 3) // 信号量,最多允许 3 个并发协程for i := 1; i <= 10; i++ {wg.Add(1)go worker(i, sem, &wg)}wg.Wait()fmt.Println("All workers finished")
}

说明:

  • sem 是一个大小为 3 的 channel,用于控制同时运行的 goroutine 数量。
  • 当信号量已满,新的协程会阻塞,直到有信号量被释放。

使用场景:

  • 控制并发协程的最大数量,避免系统资源耗尽。

10. 自旋锁(sync.Mutex 变体)

Go 官方标准库没有提供自旋锁(Spin Lock),但可以通过自定义实现。自旋锁会在获取锁时不断尝试,不释放 CPU 时间片,适用于锁占用时间非常短的场景。

示例实现

package mainimport ("fmt""runtime""sync""sync/atomic"
)type SpinLock struct {flag int32
}func (sl *SpinLock) Lock() {for !atomic.CompareAndSwapInt32(&sl.flag, 0, 1) {runtime.Gosched() // 让出 CPU}
}func (sl *SpinLock) Unlock() {atomic.StoreInt32(&sl.flag, 0)
}func main() {var lock SpinLockvar counter intvar wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func() {defer wg.Done()lock.Lock()counter++lock.Unlock()}()}wg.Wait()fmt.Println("Counter:", counter) // 输出:Counter: 5
}

说明

  • 自旋锁会反复检查锁状态,适合锁时间极短的场景。
  • 不释放 CPU 时间片可能导致资源浪费。

总结

sync 包提供了多种并发控制机制,主要包括:

  1. 互斥锁sync.Mutexsync.RWMutex
  2. 等待组sync.WaitGroup
  3. 单次执行sync.Once
  4. 条件变量sync.Cond
  5. 对象池sync.Pool
  6. 并发安全的 Mapsync.Map
  7. 原子操作sync/atomic
  8. 信号量:基于 channel 实现。
  9. 自旋锁:自定义实现,适合锁时间极短的场景。

历史文章

MySQL数据库

MySQL数据库

Redis

Redis数据库笔记合集

Golang

  1. Golang笔记——语言基础知识
  2. Golang笔记——切片与数组
  3. Golang笔记——hashmap
  4. Golang笔记——rune和byte
  5. Golang笔记——channel
  6. Golang笔记——Interface类型
  7. Golang笔记——数组、Slice、Map、Channel的并发安全性
  8. Golang笔记——协程同步
  9. Golang笔记——并发控制
  10. Golang笔记——GPM调度器
  11. Golang笔记—— new() 、 make() 和简短声明符
  12. Golang笔记—— error 和 panic
  13. Golang——内存(内存管理、内存逃逸、垃圾回收 (GC) 机制)
  14. Golang笔记——包的循环引用问题(import cycle not allowed)和匿名导入

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词