【Go】Golang Sync包

本文深入探讨了Golang的sync包,包括Mutex和RWMutex的使用,WaitGroup的并发等待,Once的单次执行,Cond的信号量机制,Pool的临时对象池,以及Map的并发安全特性。通过实例展示了它们在并发编程中的应用和重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 前言

Golang sync包提供了基础的异步操作方法,包括互斥锁Mutex执行一次Once并发等待组WaitGroup
本文主要介绍sync包提供的这些功能的基本使用方法。

  1. Mutex: 互斥锁
  2. RWMutex:读写锁
  3. WaitGroup:并发等待组
  4. Once:执行一次
  5. Cond:信号量
  6. Pool:临时对象池
  7. Map:自带锁的map

二. sync.Mutex

sync.Mutex称为互斥锁,常用在并发编程里面。互斥锁需要保证的是同一个时间段内不能有多个并发协程同时访问某一个资源(临界区)。
sync.Mutex有2个函数LockUnLock分别表示获得锁和释放锁。

func (m *Mutex) Lock()
func (m *Mutex) UnLock()

sync.Mutex初始值为UnLock状态,并且sync.Mutex常做为其它结构体的匿名变量使用。

举个例子: 我们经常使用网上支付购物东西,就会出现同一个银行账户在某一个时间既有支出也有收入,那银行就得保证我们余额准确,保证数据无误。
我们可以简单的实现银行的支出和收入来说明Mutex的使用

type Bank struct {
	sync.Mutex
	balance map[string]float64
}

// In 收入
func (b *Bank) In(account string, value float64) {
        // 加锁 保证同一时间只有一个协程能访问这段代码
	b.Lock()
	defer b.Unlock()

	v, ok := b.balance[account]
	if !ok {
		b.balance[account] = 0.0
	}

	b.balance[account] += v
}

// Out 支出
func (b *Bank) Out(account string, value float64) error {
        // 加锁 保证同一时间只有一个协程能访问这段代码
	b.Lock()
	defer b.Unlock()

	v, ok := b.balance[account]
	if !ok || v < value {
		return errors.New("account not enough balance")
	}

	b.balance[account] -= value
	return nil
}

三. sync.RWMutex

sync.RWMutex称为读写锁是sync.Mutex的一种变种,RWMutex来自于计算机操作系统非常有名的读者写者问题
sync.RWMutex目的是为了能够支持多个并发协程同时读取某一个资源,但只有一个并发协程能够更新资源。也就是说读和写是互斥的,写和写也是互斥的,读和读是不互斥的。

总结起来如下

  1. 当有一个协程在读的时候,所有写的协程必须等到所有读的协程结束才可以获得锁进行写操作。
  2. 当有一个协程在读的时候,所有读的协程不受影响都可以进行读操作。
### Golang 中 `sync` 的用法 #### 错误处理与同步原语 在 Go 语言中,`sync` 提供了基本的同步原语,如互斥锁(Mutex)、读写锁(RWMutex),以及用于 goroutine 协调的 WaitGroup 和 Once 类型。这些工具帮助开发者编写线程安全的应用程序。 对于并发编程中的错误处理,通常建议将错误返回给调用者而不是直接 panic[^1]: ```go func safeOperation() (result int, err error) { mu.Lock() defer mu.Unlock() // 执行可能失败的操作 result, err = doSomethingThatMightFail() if err != nil { return 0, fmt.Errorf("operation failed: %w", err) } return result, nil } ``` #### 使用 Mutex 实现数据保护 为了防止多个 goroutines 同时访问共享资源而导致的竞争条件,可以使用 `sync.Mutex` 或 `sync.RWMutex` 来锁定临界区。下面是一个简单的计数器例子,展示了如何利用 mutex 进行同步控制[^3]: ```go type Counter struct { value int sync.Mutex } // Increment increases counter by one. func (c *Counter) Increment() { c.Lock() defer c.Unlock() c.value++ } // Value returns current count without holding lock. func (c *Counter) Value() int { c.Lock() v := c.value c.Unlock() return v } ``` #### 利用 WaitGroup 等待一组 Goroutines 完成 当有若干个并行的任务需要完成后再继续执行后续逻辑时,可以通过创建一个 `WaitGroup` 对象来跟踪它们的状态,并等待所有任务结束之后再释放阻塞状态: ```go var wg sync.WaitGroup for i := 0; i < n; i++ { wg.Add(1) go func(i int) { defer wg.Done() // Perform work here... processItem(i) }(i) } wg.Wait() // Block until all workers are done. fmt.Println("All tasks have been completed.") ``` #### 使用 Once 确保初始化只发生一次 有时希望某些昂贵操作仅被执行一次,比如加载配置文件或建立数据库连接池等场景下非常有用。此时就可以借助于 `sync.Once` 提供的功能,在第一次调用 Do 方法的时候会触发指定函数的运行,而后的任何重复调用都不会再次执行该动作[^2]: ```go var config ConfigType var onceConfig sync.Once getConfig := func() *ConfigType { onceConfig.Do(func(){ // Load configuration from file or other sources only ONCE. config.loadFromFileSystem() }) return &config } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值