4 并发编程之sync包的应用

在上一篇文章中,我们讨论了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() // 标记该 goroutine 完成
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) // 增加 WaitGroup 计数
go worker(i, &wg)
}

wg.Wait() // 等待所有 goroutine 完成
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 包,包括 WaitGroupMutexRWMutex 等核心组件。这些工具能够帮助我们在编写并发程序时有效地管理数据的访问和同步,避免数据竞争和不一致性。在下一篇文章中,我们将探讨 Go 语言在网络编程中的应用,特别是 HTTP 编程的能力,带来更多实用的案例。继续关注我们的并发编程系列教程!

4 并发编程之sync包的应用

https://zglg.work/go-one/4/

作者

IT教程网(郭震)

发布于

2024-08-10

更新于

2024-08-11

许可协议

分享转发

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论