在上一篇文章中,我们讨论了Go语言中的并发模式与设计,深入理解了如何利用 goroutines 和 channels 来构建高效的并发程序。本篇将重点介绍 Go 语言中的 sync
包,它提供了一些用于同步的基本原语,以便在并发环境中安全地共享数据。
sync 包的核心组件
sync
包主要包含了以下几个重要的结构体,我们将逐一介绍它们的用途和应用场景:
1. WaitGroup
sync.WaitGroup
是一个同步原语,用于等待一组 goroutine 的完成。当你启动多个 goroutine 来执行并发任务时,可以使用 WaitGroup
来等待所有的任务完成。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import ( "fmt" "sync" "time" )
func worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d is starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d is done\n", id) }
func main() { var wg sync.WaitGroup
for i := 1; i <= 3; i++ { wg.Add(1) go worker(i, &wg) }
wg.Wait() fmt.Println("All workers are done.") }
|
在这个例子中,我们创建了三个并发的工作程序(worker),并使用 WaitGroup
来确保主程序在所有工作程序完成之前不会退出。
2. Mutex
sync.Mutex
是一个互斥锁,用于保护共享数据。在并发程序中,可能会有多个 goroutine 同时访问和修改共享的变量。使用 Mutex
可以确保在同一时刻只有一个 goroutine 在执行访问保护的代码块。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package main
import ( "fmt" "sync" )
var ( counter int mu sync.Mutex )
func increment(wg *sync.WaitGroup) { defer wg.Done() mu.Lock() counter++ mu.Unlock() }
func main() { var wg sync.WaitGroup
for i := 0; i < 1000; i++ { wg.Add(1) go increment(&wg) }
wg.Wait() fmt.Println("Final counter:", counter) }
|
在上面的代码中,counter
变量被多个 goroutine 并发地递增。我们使用 Mutex
来确保在递增操作期间,只有一个 goroutine 可以访问 counter
,从而避免数据竞争。
3. RWMutex
sync.RWMutex
是读写互斥锁,允许多个 goroutine 同时读取,但在写入时会限制访问。它非常适合读多写少的场景。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package main
import ( "fmt" "sync" "time" )
var ( data = make(map[int]int) mu sync.RWMutex )
func readData(key int, wg *sync.WaitGroup) { defer wg.Done() mu.RLock() value := data[key] mu.RUnlock() fmt.Printf("Read key %d: %d\n", key, value) }
func writeData(key, value int, wg *sync.WaitGroup) { defer wg.Done() mu.Lock() data[key] = value mu.Unlock() }
func main() { var wg sync.WaitGroup
for i := 0; i < 5; i++ { wg.Add(1) go writeData(i, i*100, &wg) }
wg.Wait()
for i := 0; i < 5; i++ { wg.Add(1) go readData(i, &wg) }
wg.Wait() }
|
此例中,我们通过 RWMutex
实现了对数据的安全读写。在写操作时,如果有写锁定,则无法进行读操作,确保数据的一致性。
总结
本篇文章详细介绍了 Go 语言中的 sync
包,包括 WaitGroup
、Mutex
和 RWMutex
等核心组件。这些工具能够帮助我们在编写并发程序时有效地管理数据的访问和同步,避免数据竞争和不一致性。在下一篇文章中,我们将探讨 Go 语言在网络编程中的应用,特别是 HTTP 编程的能力,带来更多实用的案例。继续关注我们的并发编程系列教程!