Go语言中sync包提供Mutex、RWMutex、Once和WaitGroup实现并发安全:1. Mutex通过加锁保护共享变量避免竞态;2. RWMutex在读多写少场景提升性能,允许多个读锁共存;3. Once确保初始化操作仅执行一次;4. WaitGroup用于主线程等待所有goroutine完成任务。

在Go语言中,sync 包提供了多种原语来实现并发安全,尤其适用于多个goroutine访问共享资源的场景。虽然Go提倡“不要通过共享内存来通信,而是通过通信来共享内存”,但在实际开发中,仍不可避免地需要使用锁机制保护数据一致性。下面介绍如何使用 sync.Mutex、sync.RWMutex、sync.Once 和 sync.WaitGroup 实现常见的并发安全操作。
当多个goroutine同时读写同一个变量时,会出现竞态问题。使用 sync.Mutex 可以确保同一时间只有一个goroutine能访问临界区。
例如,实现一个并发安全的计数器:
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
每次修改或读取 value 前都加锁,避免数据竞争。可以使用 go run -race 来检测潜在的竞态条件。
立即学习“go语言免费学习笔记(深入)”;
如果结构体主要是读操作,偶尔写入,使用 sync.RWMutex 更高效。它允许多个读锁同时存在,但写锁独占。
改造上面的计数器:
type SafeCounter struct {
mu sync.RWMutex
value int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *SafeCounter) Value() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.value
}
读操作使用 RLock(),提升高并发读场景下的性能。
在并发环境下,某些初始化逻辑(如加载配置、创建单例)应只运行一次。sync.Once 能保证函数仅执行一次,无论多少goroutine调用。
var once sync.Once
var config map[string]string
func GetConfig() map[string]string {
once.Do(func() {
config = loadConfigFromDisk()
})
return config
}
即使多个goroutine同时调用 GetConfig(),loadConfigFromDisk() 也只会执行一次。
sync.WaitGroup 用于主线程等待所有子goroutine结束,常用于并发任务编排。
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}(i)
}
wg.Wait() // 阻塞直到所有goroutine调用 Done()
fmt.Println("All workers finished")
注意:应在 goroutine 外部调用 Add(),否则可能因调度问题导致 WaitGroup 计数不准确。
基本上就这些常见模式。合理使用 sync 包中的工具,可以在不依赖 channel 的情况下写出高效、安全的并发代码。关键是理解每种锁的适用场景,避免过度加锁影响性能,也要防止漏锁导致数据竞争。
以上就是Golang如何使用sync实现并发安全_Golang sync并发安全操作实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号