golang,go,博客,开源,编程
在 Go 语言中,常见的同步原语有以下几种:
sync.Mutex
(互斥锁)Mutex
是最常用的同步原语之一,用于保证同一时间只有一个 Goroutine 能够访问某个共享资源。它有两个主要方法:
Lock()
:锁定互斥量,直到它被解锁。Unlock()
:解锁互斥量,允许其他 Goroutine 锁定它。package main
import (
"fmt"
"sync"
)
var (
mutex sync.Mutex
counter int
)
func increment() {
mutex.Lock() // 锁定互斥量
defer mutex.Unlock() // 确保解锁
counter++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
sync.RWMutex
(读写锁)RWMutex
是 Mutex
的一个扩展,允许多个 Goroutine 同时读取共享资源,但在写入时只有一个 Goroutine 可以访问资源。它有以下方法:
RLock()
:加读锁,多个 Goroutine 可以同时获取读锁。RUnlock()
:释放读锁。Lock()
:加写锁,只有一个 Goroutine 可以获取写锁。Unlock()
:释放写锁。package main
import (
"fmt"
"sync"
)
var (
rwMutex sync.RWMutex
data int
)
func readData() {
rwMutex.RLock() // 获取读锁
defer rwMutex.RUnlock() // 释放读锁
fmt.Println("Reading data:", data)
}
func writeData(newData int) {
rwMutex.Lock() // 获取写锁
defer rwMutex.Unlock() // 释放写锁
data = newData
fmt.Println("Writing data:", data)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
readData()
}()
}
wg.Add(1)
go func() {
defer wg.Done()
writeData(42)
}()
wg.Wait()
}
sync.WaitGroup
(等待组)WaitGroup
用于等待一组 Goroutine 完成。在多个 Goroutine 并发执行时,可以使用 WaitGroup
来等待所有任务完成。
Add(delta int)
:增加等待的 Goroutine 数量。Done()
:通知 WaitGroup
该 Goroutine 完成。Wait()
:阻塞直到 WaitGroup
中的计数器减到 0。package main
import (
"fmt"
"sync"
)
func task(wg *sync.WaitGroup, id int) {
defer wg.Done() // 完成时减少计数
fmt.Printf("Task %d is starting\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go task(&wg, i)
}
wg.Wait() // 等待所有任务完成
fmt.Println("All tasks are done!")
}
sync.Once
(一次性执行)Once
确保某些操作只执行一次,通常用于初始化某些资源。例如,在并发环境中,可能需要保证某些初始化操作只执行一次,而不被多个 Goroutine 重复执行。
Do(f func())
:执行 f
函数,确保只执行一次。package main
import (
"fmt"
"sync"
)
var once sync.Once
func initialize() {
fmt.Println("Initializing resources...")
}
func main() {
for i := 0; i < 5; i++ {
go func() {
once.Do(initialize) // 确保只初始化一次
}()
}
}
sync/atomic
(原子操作)sync/atomic
包提供了一些原子操作,常用于多线程环境下对基本数据类型进行操作而不需要加锁。这些操作能够保证线程安全,避免使用更重的同步原语。
atomic.AddInt32
, atomic.AddInt64
: 原子加法。atomic.CompareAndSwapInt32
, atomic.CompareAndSwapInt64
: 原子比较并交换。atomic.LoadInt32
, atomic.LoadInt64
: 原子读取。package main
import (
"fmt"
"sync"
"sync/atomic"
)
var counter int32
func increment() {
atomic.AddInt32(&counter, 1)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
channel
(通道)Go 的通道不仅是数据交换的工具,还可以用于同步操作。通过通道发送和接收数据,可以在多个 Goroutine 之间进行同步。
package main
import (
"fmt"
)
func task(ch chan bool) {
// 任务完成后发送信号
fmt.Println("Task is complete")
ch <- true
}
func main() {
ch := make(chan bool)
go task(ch)
<-ch // 阻塞,等待任务完成
fmt.Println("Main function finished")
}
这些同步原语帮助我们在 Go 中高效、安全地处理并发编程。通过合理选择适当的同步原语,可以避免竞态条件、死锁等问题,提高程序的稳定性和性能。