章节

11.1 线程安全

本篇学习线程安全与数据竞争的基本概念,并能使用原子操作保护简单计数器。

线程安全

概念说明

当多个 goroutine 同时访问同一份共享数据时,如果至少有一个 goroutine 在写数据,就可能产生数据竞争。
数据竞争会让结果不稳定,也可能让程序在运行时出现难以复现的问题。

线程安全的目标是:
多个并发任务同时执行时,共享数据仍然保持正确状态。

语法/规则

  1. 并发读取同一份不可变数据通常是安全的。
  2. 并发读写或并发写同一个变量时,需要同步保护。
  3. 简单数值计数可以使用 sync/atomic
  4. 复杂临界区通常使用 sync.Mutex
  5. 可以使用 go test -racego run -race 检测数据竞争。

原子计数示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	var count int64         // 共享计数器,多个 goroutine 都会修改它
	var wg sync.WaitGroup   // 用来等待前面启动的 goroutine 全部执行完成

	for i := 0; i < 3; i++ {
		wg.Add(1) // 每启动一个 goroutine,就先登记 1 个待完成任务
		go func() {
			defer wg.Done() // defer 不是立刻执行;它会在当前 goroutine 结束前执行 Done
			atomic.AddInt64(&count, 1) // 安全地把 count 加 1,避免多个 goroutine 同时修改出错
		}()
	}

	wg.Wait()          // 等待前面 3 个 goroutine 都调用 Done
	fmt.Println(count) // 所有 goroutine 都结束后,再输出最终结果
}

输出结果:

1
3

这里的 defer wg.Done() 怎么理解

先看这行代码:

1
defer wg.Done()

在这个示例里,可以先把它理解成:

1
这个 goroutine 快结束时再执行 wg.Done()

也就是说,它不是一写到这里就立刻执行。
而是先把 wg.Done() 记下来,等当前 goroutine 里的代码快执行完时再调用。

所以这段 goroutine 更接近下面这种理解:

1
2
3
4
go func() {
	atomic.AddInt64(&count, 1)
	wg.Done()
}()

只是写成 defer wg.Done() 更安全。
因为即使后面 goroutine 里的代码变多了,Done() 也更不容易漏写。

这两个包在这里是做什么的

示例里导入了这两个包:

1
2
3
4
import (
	"sync"
	"sync/atomic"
)

在这篇里可以先这样理解:

  1. sync 是 Go 标准库里处理并发同步的包。
  2. 这篇示例里,sync 只用到了 WaitGroup
  3. WaitGroup 的作用是:等待前面启动的所有 goroutine 都执行完成。

对应到代码里就是:

1
2
3
4
var wg sync.WaitGroup
wg.Add(1)
wg.Done()
wg.Wait()

sync/atomic 也可以先这样理解:

  1. sync/atomic 是 Go 标准库里提供原子操作的包。
  2. 这篇示例里,sync/atomic 只用到了 atomic.AddInt64(&count, 1)
  3. 它的作用是:安全地把 count 加 1,避免多个 goroutine 同时修改时出现数据竞争。

对应到代码里就是:

1
atomic.AddInt64(&count, 1)

可以先把它理解成:

1
安全地执行 count += 1

所以在这个示例中:

  1. sync.WaitGroup 负责等所有 goroutine 做完。
  2. sync/atomic 负责让多个 goroutine 安全地修改同一个计数器。

常见错误

  1. 认为 count++ 是一个原子操作,实际它包含读取、计算、写回多个步骤。
  2. 只在写入时加锁,读取时不加锁,仍可能产生数据竞争。
  3. 不使用 -race 检测并发代码,导致问题只在压力场景中暴露。
本文禁止转载
使用 Hugo 构建
主题 StackJimmy 设计 由 Hobin 魔改
最近构建时间:2026-04-17 19:07:48 CST
载入天数...载入时分秒...
发表了 1 篇文章 · 发表了 152 篇笔记 · 总计 18 万 0 千字