Go语言学习笔记 | Sync包与同步原语

2024-04-16 13:01:02 浏览数 (1)

一、引言

并发模型和Go语言的核心特性之一,Go语言的并发模型主要基于goroutineschannelgoroutine是由Go运行时管理的轻量级线程,它们使用非常少的内存,并且可以快速地创建和销毁。channel则是用于在goroutines之间传递消息的管道,它们可以是同步的也可以是异步的,为数据交换提供了一种安全且简单的方式。

然而,并非所有的并发问题都最适合用channels来解决。在某些情况下,直接使用同步原语来控制对共享资源的访问会更加高效和直接。

Go的标准库sync提供了多种同步工具,包括互斥锁(Mutex)、读写锁(RWMutex)、等待组(WaitGroup)和一次性执行(Once)等,这些都是我们将在本文中深入探讨的主题。

本文旨在介绍Go语言中的同步原语和锁,解释它们的工作原理,以及如何在实际编程中正确地使用它们。

二、同步原语:标准库sync

Go 语言在 sync 包中提供了一些同步原语,包括常见的 sync.Mutex、sync.RWMutex、sync.WaitGroup、sync.Once 和 sync.Cond:

[sync.Mutex](https://draveness.me/golang/tree/sync.Mutex) (互斥锁)

  • Mutex是最基本的同步原语之一,用于保护共享资源,防止多个goroutine在同一时间对同一资源进行读写,从而避免竞态条件。
  • Mutex提供了LockUnlock方法,用于在访问共享资源前后加锁和解锁。当一个goroutine获得了Mutex锁,其他尝试获取该锁的goroutine会阻塞,直到锁被释放。

sync.RWMutex(读写锁)

  • RWMutex是一种特殊类型的互斥锁,它允许多个goroutine同时读取共享资源,但在写入时需要独占访问。
  • RWMutex提供了RLockRUnlock方法用于读操作的锁定和解锁,以及LockUnlock方法用于写操作。这种锁机制在读多写少的场景下非常有用,因为它可以提高并发性能。

WaitGroup

  • WaitGroup用于等待一组goroutine完成。它在协调多个goroutine执行结束时非常有用,比如在主goroutine中等待一组工作goroutine完成任务。
  • 通过Add方法设置计数器,每启动一个工作goroutine就增加计数。工作goroutine完成后调用Done(本质上是Add(-1))来减少计数器。Wait方法会阻塞调用它的goroutine,直到计数器为零。

Once

  • Once是一个同步原语,它能保证在多个goroutine中只有一个能执行某个操作,且只执行一次。这在初始化共享资源或执行只需要运行一次的设置代码时非常有用。
  • Once只有一个方法Do,它接收一个函数作为参数,确保这个函数在程序运行期间只被执行一次,无论它被多少个goroutine调用。

Cond(条件变量)

  • Cond实现了条件变量,一个能够阻塞goroutine直到某个条件为真的同步原语。条件变量总是与互斥锁(Mutex)一起使用,以避免竞态条件。
  • Cond提供了Wait方法来挂起当前goroutine,直到被SignalBroadcast方法唤醒。Signal唤醒等待队列中的一个goroutine,而Broadcast唤醒所有等待的goroutine。

三、同步原语与Channel比较

Channel应用场景

Channel是一种用于在不同的goroutine之间进行通信和同步的机制。适用场景包括:

  • 在多个goroutine之间传递数据或消息。
  • 实现生产者-消费者模式,其中一个goroutine负责生产数据,另一个或多个goroutine负责消费数据。
  • 实现并发任务的协调和同步。

同步原语的应用场景

同步原语是一种用于控制并发访问共享资源的机制,如锁、条件变量等。适用场景包括:

  • 在多个goroutine之间对共享资源进行互斥访问,确保数据的一致性和正确性。
  • 控制并发执行的顺序,如使用互斥锁来实现临界区的互斥访问。
  • 实现线程间的等待和通知机制,如使用条件变量来实现等待和唤醒操作。

四、高级同步技术

原子操作(sync/atomic包)

原子操作是一种无需锁定的并发编程技术,可以保证对共享变量的操作是原子性的。在Go语言中,可以使用sync/atomic包提供的原子操作函数来实现。常见的原子操作包括原子增减、原子交换和原子比较交换等。原子操作适用于需要对共享变量进行简单的读写操作,并且不需要复杂的同步机制。

信号量模式(semaphore

信号量是一种用于控制并发访问资源的同步机制。它可以限制同时访问某个资源的线程或协程的数量。在Go语言中,可以使用channel或sync包中的WaitGroup来实现信号量模式。通过控制信号量的数量,可以实现对资源的并发访问控制,避免资源过度竞争和冲突。

Barrier(屏障):

屏障是一种同步机制,用于确保多个线程或协程在某个点上同步等待,直到所有线程都到达该点后才能继续执行。屏障可以用于解决多个线程或协程之间的协调问题,例如在并行计算中,当所有计算任务完成后,才能进行下一步的操作。在Go语言中,可以使用sync包中的WaitGroup来实现屏障。

0 人点赞