别着急,坐和放宽
使用社交账号登录
sync 包通过直接操作内存或利用 OS 信号量实现同步,是构建高性能并发系统的基石。
Add 增加,Done 减少,Wait 阻塞直到归零。Add 必须发生在 go 启动之前,防止 Wait 提前返回。uint32 标志位和 Mutex 实现。确保目标函数仅执行一次,常用于单例模式。Wait 会释放锁并挂起,被唤醒后会重新竞争锁。sync.Map 并非简单的 map + Mutex,它通过“空间换时间”和“读写分离”的思想,针对特定场景做了极致优化。
1.6.1 核心分层架构
atomic.Value 存储的 readOnly 结构体。amended 标志,记录 dirty 层是否有 read 层没有的数据。map[interface{}]*entry。read 层缺失的数据在此。*entry 中,通过原子操作(CAS)更新,实现 read 和 dirty 共享同一份数据值。1.6.2 数据流转“生命周期”
Read 命中 → 直接返回(无锁,快)。Read 缺失 + amended 为真 → 加锁查 Dirty,并记录一次 miss。Read 中 → 尝试原子更新(CAS)。Read 或原子更新失败 → 加锁操作 Dirty。miss 计数达到 len(dirty) 时,Dirty 整体晋升为 Read,随后 Dirty 重置为 nil。Read,若存在则通过 entry.delete() 进行逻辑删除(标记位);若只在 Dirty 中,则加锁物理删除。1.6.3 为什么它快?
ReadOnly 层分流了绝大部分读请求。sync/atomic 绕过信号量,直接利用 CPU 原子指令实现同步。
atomic.LoadInt32 和 StoreInt32 实现极低开销的服务开关。atomic.Value 支持原子性地替换复杂结构体。CompareAndSwap 是无锁编程的核心。Context 传播取消信号。recover 机制。解决方案 A:显式参数传递(推荐)
解决方案 B:局部变量屏蔽(Shadowing)
注:Go 1.22+ 版本已在编译器层面优化了 for 循环变量的生命周期,但为了代码的兼容性与严谨性,建议养成上述习惯。
context.WithTimeout。| 需求场景 | 推荐工具 | 核心优势 |
|---|---|---|
| 任务流同步 | WaitGroup | 计数等待,简单可靠 |
| 读多写少 | RWMutex | 共享读锁,提升吞吐 |
| 单例初始化 | sync.Once | 原子保障,仅执行一次 |
| 条件通知 | sync.Cond | 避免死循环轮询 |
| 高频临时对象 | sync.Pool | 缓解 GC 压力 |
| 稳定 Key 并发读写 | sync.Map | 减少锁竞争,无锁读优化 |
| 极高性能计数 | atomic | 硬件级原子指令 |
Go 的并发编程精髓在于:理解原语的代价,敬畏并发的复杂性,永远为 Goroutine 留好退路。
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 <= 3; i++ {
wg.Add(1) // 启动前加 1
go worker(i, &wg)
}
wg.Wait() // 等待所有任务结束
}
type SafeCounter struct {
mu sync.RWMutex
stats map[string]int
}
func (c *SafeCounter) Read(key string) int {
c.mu.RLock() // 读锁,多个 Goroutine 可同时进入
defer c.mu.RUnlock()
return c.stats[key]
}
func (c *SafeCounter) Write(key string, val int) {
c.mu.Lock() // 写锁,排他
defer c.mu.Unlock()
c.stats[key] = val
}
var (
once sync.Once
instance *Database
)
func GetDB() *Database {
once.Do(func() {
instance = &Database{Conn: "Initialized"}
fmt.Println("DB Instance Created")
})
return instance
}
func condExample() {
l := sync.NewCond(&sync.Mutex{})
ready := false
go func() {
time.Sleep(time.Second)
l.L.Lock()
ready = true
l.L.Unlock()
l.Broadcast() // 唤醒所有等待者
}()
l.L.Lock()
for !ready { // 必须在 for 循环中检查,防止虚假唤醒
l.Wait()
}
fmt.Println("Ready now!")
l.L.Unlock()
}
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func log(data string) {
b := bufPool.Get().(*bytes.Buffer)
b.Reset()
b.WriteString(data)
// 使用完毕后放回
bufPool.Put(b)
}
func SafeGo(f func()) {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic recovered: %v", err)
}
}()
f()
}()
}
for i := 0; i < 5; i++ {
go func(id int) {
fmt.Println(id) // id 是局部副本
}(i)
}
for i := 0; i < 5; i++ {
i := i // 在循环体内重新赋值,为每轮迭代创建独立副本
go func() {
fmt.Println(i) // 这里的 i 指向当前迭代的副本地址
}()
}