Skip to content

Commit

Permalink
feat(concurrency): add sync.Mutex:data race
Browse files Browse the repository at this point in the history
  • Loading branch information
shgopher committed Nov 30, 2023
1 parent 77529f9 commit 60d5f04
Showing 1 changed file with 76 additions and 1 deletion.
77 changes: 76 additions & 1 deletion 并发/同步原语/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @Author: shgopher [email protected]
* @Date: 2023-05-14 23:08:19
* @LastEditors: shgopher [email protected]
* @LastEditTime: 2023-11-29 22:14:21
* @LastEditTime: 2023-11-30 23:43:50
* @FilePath: /GOFamily/并发/同步原语/README.md
* @Description:
*
Expand Down Expand Up @@ -79,6 +79,81 @@ type Locker interface{
Unlock()
}
```
Mutex 就实现了这个 Locker 接口

Locker 定义了锁的基本方法,加锁 + 解锁

### 什么是 data race 的本质
我们常说 data race 的情况,是多线程同时对于某块内存进行数据的变更,那么问题来了,这个地方谈到的同时,是真的物理层面的同时还是近似同时?

关于 data race 中的 “同时” 通常是**指逻辑上的同时或近似同时,而不是物理层面严格的同一时刻。**

主要原因有以下几点:

- 现代 CPU 中,同一时刻真正执行指令的只有一个核心。不同核心之间以及同一核心的不同执行周期之间,不存在物理层面严格的同步。
- 即使在单核 CPU 上,由于指令流水线、内存缓存、分支预测等机制,实际执行顺序也可能与代码顺序不一致,很难定义物理层面严格的同步。
- 不同线程之间进行切换的时间间隔非常小 (几十到几百纳秒),对程序逻辑而言可以视为同时进行。
- 要构成 data race,不同线程对同一地址的访问之间间隔不能太长,必须在一个指令/操作的启始和完成之间,所以也符合逻辑上的近似同时。

所以,data race 中的 “同时” 就是指逻辑上近似同时,或者无法确定准确执行顺序的情况,而不是物理层面严格同一时刻。这种近似同时从程序角度就是可能造成冲突,需要进行同步处理。
### 检测 data race 的工具
并发问题不是一定能肉眼看出来的,如果只是基础的,容易看出来的也就罢了,但是很多隐藏的 data race 问题必须使用专业的工具去鉴别,go 语言提供了 `-race` 功能,在编译,测试,run 的时候,会自动检测到 data race 问题,并且给出详细的错误信息。

```bash
go run -race main.go
```

我们看一个例子
```go
func main() {
value := 0
for i := 0; i < 10000; i++ {
go func(){
value ++
}()
}
time.Sleep(1000)
fmt.Println(value)
}
```
本能的你会以为能输出 10000,但是结果确实 9000 多,而且还不一定,这是为啥呢?

因为你以为 ++ 操作是原子操作,其实并不是。

++ 操作分为三个步骤
- 获取 value 值
- 值+1
- 将新值写回

这其实是三个步骤,10000 个线程,假如同时有 10 个去读了这个 value,在他们看来都是初始值是 0,然后他们+1,然后写回去了结果 value 是 1,相当于 10 个 goroutine 都去写,本来应该是 10,但是赋值回去都变成了 1

这个时候,如果你使用 run -race 就能检测出来

```bash
go1 go run -race main.go
==================
WARNING: DATA RACE
Read at 0x00c00010a018 by goroutine 8:
main.main.func1()
/Users/shgopher/Desktop/1/go1/main.go:23 +0x2c

Previous write at 0x00c00010a018 by goroutine 6:
main.main.func1()
/Users/shgopher/Desktop/1/go1/main.go:23 +0x3c

Goroutine 8 (running) created at:
main.main()
/Users/shgopher/Desktop/1/go1/main.go:22 +0x48

Goroutine 6 (finished) created at:
main.main()
/Users/shgopher/Desktop/1/go1/main.go:22 +0x48
==================
9957
Found 1 data race(s)
exit status 66
```
多线程多某个区域的内存进行同时 (或者近似同时) 操作,这就是数据竞争

### sync.Mutex 互斥锁的实现原理
go 语言互斥锁的实现非常简单,只有这一个结构体就是核心:
Expand Down

0 comments on commit 60d5f04

Please sign in to comment.