Mutex解决数据竞态,确保共享资源的独占访问;WaitGroup用于等待一组协程完成,二者协同实现并发控制。

在Go语言的并发世界里,
sync
Mutex
WaitGroup
Mutex
WaitGroup
Go语言的
sync
Mutex
WaitGroup
互斥锁(sync.Mutex
互斥锁的核心思想是“互斥”,即在任何给定时刻,只有一个协程能够持有锁,从而访问被保护的共享资源。当一个协程尝试获取已被其他协程持有的锁时,它会被阻塞,直到锁被释放。
立即学习“go语言免费学习笔记(深入)”;
基本用法:
sync.Mutex
Lock()
Unlock()
Lock()
Unlock()
defer
应用场景: 保护共享变量、数据结构(如
map
slice
代码示例:
package main
import (
"fmt"
"sync"
"time"
)
var (
counter int
mu sync.Mutex // 声明一个互斥锁
)
func increment() {
mu.Lock() // 获取锁
defer mu.Unlock() // 确保锁在函数退出时被释放
counter++
fmt.Printf("Incremented counter to %d\n", counter)
}
func main() {
fmt.Println("Mutex Example:")
for i := 0; i < 5; i++ {
go increment()
}
time.Sleep(time.Millisecond * 100) // 等待goroutine完成
fmt.Printf("Final counter value: %d\n", counter)
}等待组(sync.WaitGroup
等待组用于等待一组协程的完成。它提供了一种简单而有效的方式来同步主协程和它启动的子协程。
基本用法:
sync.WaitGroup
Add()
Done()
Wait()
Add(delta int)
delta
Done()
Wait()
应用场景: 当主协程需要启动多个子协程并行执行任务,并且需要在所有子协程都完成后才能继续执行后续逻辑时。例如,一个批处理系统,需要等待所有文件处理协程都完成后再生成报告。
代码示例:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 协程完成时调用Done()
fmt.Printf("Worker %d starting...\n", id)
time.Sleep(time.Millisecond * 50) // 模拟工作
fmt.Printf("Worker %d finished.\n", id)
}
func main() {
fmt.Println("\nWaitGroup Example:")
var wg sync.WaitGroup // 声明一个等待组
for i := 1; i <= 3; i++ {
wg.Add(1) // 每次启动一个协程,计数器加1
go worker(i, &wg)
}
wg.Wait() // 阻塞直到所有协程完成
fmt.Println("All workers finished. Main goroutine continues.")
}sync.Mutex
在并发编程中,
sync.Mutex
举个例子,假设账户余额是100元。
最终余额可能是110或120,而不是期望的130。这就是数据竞态,多个协程在没有同步的情况下,同时访问并修改共享数据,导致程序行为不可预测,结果错误。这种错误很难复现,因为它们依赖于协程的调度时机,调试起来简直是噩梦。
sync.Mutex
可以说,
Mutex
sync.WaitGroup
sync.WaitGroup
WaitGroup
那么,它和通道(Channel)有什么不同呢?这是一个非常好的问题,因为两者都涉及协程间的同步,但它们的侧重点和使用场景有显著区别:
目的不同:
WaitGroup
Channel
struct{}数据传输能力:
WaitGroup
Channel
使用模式:
WaitGroup
Add
Done
Wait
Channel
复杂性与开销:
WaitGroup
Channel
总结来说: 当你只是想知道“所有这些任务都搞定了没?”而不需要从这些任务中收集数据或进行复杂通信时,
WaitGroup
Channel
WaitGroup
Channel
尽管
Mutex
WaitGroup
sync.Mutex
死锁(Deadlock): 这是最常见的陷阱。
Unlock()
Lock()
Unlock()
defer mu.Unlock()
Mutex
Lock()
锁粒度过粗: 将大段代码用一个锁保护起来,导致并发度下降,性能变差。虽然保证了正确性,但牺牲了效率。
锁粒度过细: 保护不足,仍然可能出现数据竞态。
sync.WaitGroup
Add()
Wait()
Wait()
Add()
Wait()
Add()
Wait()
Add()
Done()
Done()
Add()
WaitGroup
Wait()
Done()
Add()
WaitGroup
Add(1)
defer wg.Done()
Add()
Add()
go func()
Add()
Wait()
wg.Add(1)
go
通用优化思考:
sync.RWMutex
sync.RWMutex
sync/atomic
sync/atomic
Mutex
context
context.Context
理解这些陷阱并掌握优化技巧,能帮助你写出更健壮、更高效的Go并发程序。并发编程确实需要细心和经验,但Go提供的这些工具,无疑大大降低了门槛。
以上就是Golang sync包常用组件 互斥锁与等待组应用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号