在Go语言中,sync
(同步)库是用于处理并发编程的重要工具。它提供了一系列的原语,用于协调多个goroutine之间的执行。在本文中,我们将深入探讨sync
库的一些关键组件,包括Mutex
、RWMutex
、WaitGroup
和Cond
,并通过实例演示它们的用法。
1. Mutex(互斥锁)
Mutex
是最基本的同步原语之一,用于保护共享资源,确保在同一时刻只有一个goroutine可以访问它。下面是一个简单的示例:
package main
import (
"fmt"
"sync"
"time"
)
var counter int
var mutex sync.Mutex
func increment() {
mutex.Lock()
defer mutex.Unlock()
counter++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
在上面的例子中,Mutex
用于锁定counter
变量,以确保在任何时刻只有一个goroutine能够递增它。WaitGroup
用于等待所有goroutine完成后再继续执行主程序。
2. RWMutex(读写互斥锁)
RWMutex
是对Mutex
的扩展,允许多个goroutine同时读取共享资源,但在写入时会互斥。这在读多写少的场景中特别有用。下面是一个示例:
package main
import (
"fmt"
"sync"
"time"
)
var (
data map[string]string
rwMutex sync.RWMutex
)
func readData(key string) string {
rwMutex.RLock()
defer rwMutex.RUnlock()
return data[key]
}
func writeData(key, value string) {
rwMutex.Lock()
defer rwMutex.Unlock()
data[key] = value
}
func main() {
data = make(map[string]string)
var wg sync.WaitGroup
// 写入数据
for i := 0; i < 5; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
key := fmt.Sprintf("key%d", index)
value := fmt.Sprintf("value%d", index)
writeData(key, value)
time.Sleep(time.Millisecond * 100) // 模拟写入耗时
}(i)
}
// 读取数据
for i := 0; i < 10; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
key := fmt.Sprintf("key%d", index%5)
value := readData(key)
fmt.Printf("Read: %s - %s\n", key, value)
}(i)
}
wg.Wait()
}
在上述例子中,RWMutex
用于保护对data
的读写操作。多个goroutine可以同时读取,但在写入时会相互互斥。
3. WaitGroup
WaitGroup
用于等待一组goroutine完成执行。它在主goroutine中等待所有其他goroutine执行完成后再继续执行。下面是一个简单的例子:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers done")
}
在上面的例子中,WaitGroup
确保所有的worker
goroutine都执行完成后,才会继续执行主goroutine。
4. Cond(条件变量)
Cond
提供了一种在goroutine之间等待或发信号的机制。下面是一个使用Cond
的例子:
package main
import (
"fmt"
"sync"
"time"
)
var (
count int
maxCount = 3
cond *sync.Cond
)
func producer() {
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
cond.L.Lock()
count++
fmt.Printf("Produced: %d\n", count)
cond.Signal() // 发送信号通知消费者
cond.L.Unlock()
}
}
func consumer() {
for {
cond.L.Lock()
for count < 1 {
cond.Wait() // 等待生产者发信号
}
fmt.Printf("Consumed: %d\n", count)
count--
cond.L.Unlock()
}
}
func main() {
cond = sync.NewCond(&sync.Mutex{})
go producer()
go consumer()
time.Sleep(10 * time.Second)
}
在上述例子中,Cond
用于在生产者和消费者之间同步数据。生产者每秒生产一个数据,而消费者则等待信号并消费数据。
通过以上实例,我们深入了解了sync
库的关键组件及其用法。这些工具在Go语言的并发编程中扮演着重要的角色,帮助我们更安全、更有效地处理多个goroutine之间的协作。希望这篇博客能够帮助你更好地理解和运用sync
库。