Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0875105

Browse files
authored
Merge pull request #1924 from polaris1119/master
首发于:https://studygolang.com/articles/35385
2 parents 7302122 + 11523f9 commit 0875105

File tree

1 file changed

+43
-42
lines changed

1 file changed

+43
-42
lines changed

translated/tech/20200801-Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package.md renamed to published/tech/20200801-Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package.md

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1+
首发于:https://studygolang.com/articles/35385
12

2-
![](https://gitee.com/double12gzh/wiki-pictures/raw/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/%E5%9B%BE0.png)
3+
# 如何使用 atomic 包减少锁冲突
34

4-
# Go如何减少atomic包的锁冲突
5+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/1.png)
56

67
## 写在前面
78

8-
> 本文基于GoLang 1.14
9+
> 本文基于 Golang 1.14
910
10-
Go提供了channel或mutex等内存同步机制,有助于解决不同的问题。在共享内存的情况下,mutex可以保护内存不发生数据竞争(data race)。不过,虽然存在两个mutex,但Go也通过`atomic`包提供了原子内存基元来提高性能。在深入研究解决方案之前,我们先回过头来看看数据竞争。
11+
Go 提供了 channel 或 mutex 等内存同步机制,有助于解决不同的问题。在共享内存的情况下,mutex 可以保护内存不发生数据竞争(data race)。不过,虽然存在两个 mutex,但 Go 也通过 `atomic` 包提供了原子内存基元来提高性能。在深入研究解决方案之前,我们先回过头来看看数据竞争。
1112

1213
## 数据竞争
1314

14-
当两个或两个以上的goroutine同时访问同一块内存区域,并且其中至少有一个在写时,就会发生数据竞争。虽然`map`有内部有一定的机制来防止数据竞争,但一个简单的结构体并没有任何的机制,因此容易发生数据竞争。
15+
当两个或两个以上的 goroutine 同时访问同一块内存区域,并且其中至少有一个在写时,就会发生数据竞争。虽然 `map` 内部有一定的机制来防止数据竞争,但一个简单的结构体并没有任何的机制,因此容易发生数据竞争。
1516

16-
为了说明数据竞争,我以一个**goroutine持续更新的配置**为例向大家展示一下。
17+
为了说明数据竞争,我以一个**goroutine 持续更新的配置**为例向大家展示一下。
1718

18-
```golang
19+
```go
1920
package main
2021

2122
import (
@@ -30,7 +31,7 @@ type Config struct {
3031
func main() {
3132
cfg := &Config{}
3233

33-
// 启动一个writer goroutine,不断写入数据
34+
// 启动一个 writer goroutine,不断写入数据
3435
go func() {
3536
i := 0
3637

@@ -40,7 +41,7 @@ func main() {
4041
}
4142
}()
4243

43-
// 启动多个reader goroutine,不断获取数据
44+
// 启动多个 reader goroutine,不断获取数据
4445
var wg sync.WaitGroup
4546
for n := 0; n < 4; n++ {
4647
wg.Add(1)
@@ -66,7 +67,7 @@ F:\hello>go run main.go
6667
&main.Config{a:[]int{181607, 181617, 181624, 181631, 181636, 181643}}
6768
```
6869

69-
我们可以在运行时加入参数`--race`看一下结果:
70+
我们可以在运行时加入参数 `--race` 看一下结果:
7071

7172
```bash
7273

@@ -77,7 +78,7 @@ F:\hello>go run --race main.go
7778
&main.Config{a:[]int(nil)}
7879
WARNING: DATA RACE&main.Config{a:[]int(nil)}
7980

80-
Read at 0x00c00000c210 by goroutine 9:
81+
Read at 0x00c00000c210 by Goroutine 9:
8182
reflect.Value.Int()
8283
D:/Go/src/reflect/value.go:988 +0x3584
8384
fmt.(*pp).printValue()
@@ -103,27 +104,27 @@ Previous write at 0x00c00000c210 by goroutine 7:
103104
main.main.func1()
104105
F:/hello/main.go:21 +0x66
105106

106-
Goroutine 9 (running) created at:
107+
goroutine 9 (running) created at:
107108
main.main()
108109
F:/hello/main.go:29 +0x124
109110

110-
Goroutine 7 (running) created at:
111+
goroutine 7 (running) created at:
111112
main.main()
112113
F:/hello/main.go:16 +0x95
113114
==================
114115
```
115116

116-
为了避免同时读写过程中产生的数据竞争最常采用的方法可能是使用`mutex``atomic`包。
117+
为了避免同时读写过程中产生的数据竞争最常采用的方法可能是使用 `mutex``atomic` 包。
117118

118-
## Mutex?还是Atomic
119+
## Mutex?还是 Atomic
119120

120-
标准库为`sync`包提供了两种`mutex`**sync.Mutex****sync.RWMutex**。后者在你的程序需要处理多个读操作和极少的写操作时进行了优化。
121+
标准库在 `sync` 包提供了两种互斥锁 **sync.Mutex****sync.RWMutex**。后者在你的程序需要处理多个读操作和极少的写操作时进行了优化。
121122

122123
针对上面代码中产生的数据竞争问题,我们看一下,如何解决呢?
123124

124-
### 使用`sync.Mutex`解决数据竞争
125+
### 使用 `sync.Mutex` 解决数据竞争
125126

126-
```golang
127+
```go
127128
package main
128129

129130
import (
@@ -140,7 +141,7 @@ func main() {
140141
cfg := &Config{}
141142
var mux sync.RWMutex
142143

143-
// 启动一个writer goroutine,不断写入数据
144+
// 启动一个 writer goroutine,不断写入数据
144145
go func() {
145146
i := 0
146147

@@ -153,7 +154,7 @@ func main() {
153154
}
154155
}()
155156

156-
// 启动多个reader goroutine,不断获取数据
157+
// 启动多个 reader goroutine,不断获取数据
157158
var wg sync.WaitGroup
158159
for n := 0; n < 4; n++ {
159160
wg.Add(1)
@@ -172,7 +173,7 @@ func main() {
172173
}
173174
```
174175

175-
通过上面的代码,我们做了两处改动。第一处改动在写数据前通过`mux.Lock()`加了一把锁;第二处改动在读数据前通过`mux.RLock()`加了一把读锁。
176+
通过上面的代码,我们做了两处改动。第一处改动在写数据前通过 `mux.Lock()` 加了一把锁;第二处改动在读数据前通过 `mux.RLock()` 加了一把读锁。
176177

177178
运行上述代码看一下结果:
178179

@@ -190,9 +191,9 @@ F:\hello>go run --race main.go
190191

191192
这次达到了我们的预期并且也没有产生数据竞争。
192193

193-
### 使用`atomic`解决数据竞争
194+
### 使用 `atomic` 解决数据竞争
194195

195-
```golang
196+
```go
196197
package main
197198

198199
import (
@@ -237,7 +238,7 @@ func main() {
237238
}
238239
```
239240

240-
这里我们使用了`atomic`包,通过运行我们发现,也同样达到了我们期望的结果:
241+
这里我们使用了 `atomic` 包,通过运行我们发现,也同样达到了我们期望的结果:
241242

242243
```bash
243244
[...]
@@ -247,17 +248,16 @@ main.Config{a:[]int{219826, 219827, 219828, 219829, 219830, 219831}}
247248
main.Config{a:[]int{219948, 219949, 219950, 219951, 219952, 219953}}
248249
```
249250

250-
从生成的输出结果而言,看起来使用`atomic`包的解决方案要快得多,因为它可以生成更高的数字序列。
251+
从生成的输出结果而言,看起来使用 `atomic` 包的解决方案要快得多,因为它可以生成更高的数字序列。
251252
为了更加严谨的证明这个结果,我们下面将对这两个程序进行基准测试。
252253

253254
## 性能分析
254255

255-
一个benchmark应该根据被测量的内容来解释。因此,我们假设之前的程序,有一个不断存储新配置的`数据写入器`,同时也有多个不断读取配置的`数据读取器`。为了涵盖更多潜在的场景,我们还将包括一个只有`数据读取器`的benchmark,假设Config不经常改变
256+
一个 benchmark 应该根据被测量的内容来解释。因此,我们假设之前的程序,有一个不断存储新配置的 ` 数据写入器 `,同时也有多个不断读取配置的 ` 数据读取器 `。为了涵盖更多潜在的场景,我们还将包括一个只有 ` 数据读取器 ` 的 benchmark,假设 Config 不经常改变
256257

257-
下面是部分benchmark的代码:
258-
259-
```golang
258+
下面是部分 benchmark 的代码:
260259

260+
```go
261261
func BenchmarkMutexMultipleReaders(b *testing.B) {
262262
var lastValue uint64
263263
var mux sync.RWMutex
@@ -295,35 +295,36 @@ MutexOneWriterMultipleReaders-4 717ns ± 3%
295295
MutexMultipleReaders-4 176ns ± 2%
296296
```
297297

298-
基准测试证实了我们之前看到的性能情况。为了了解mutex的瓶颈到底在哪里,我们可以在启用`tracer`的情况下重新运行程序。
298+
基准测试证实了我们之前看到的性能情况。为了了解 mutex 的瓶颈到底在哪里,我们可以在启用 `tracer` 的情况下重新运行程序。
299299

300-
> 更多关于`tracer`的内容,请参考[trace](https://medium.com/a-journey-with-go/go-discovery-of-the-trace-package-e5a821743c3c)这篇文章。
300+
> 更多关于 `tracer` 的内容,请参考[trace](https://medium.com/a-journey-with-go/go-discovery-of-the-trace-package-e5a821743c3c)这篇文章。
301301
302-
下图是使用`atomic`包时,使用`pprof`分析后得到profile结果
302+
下图是使用 `atomic` 包时,使用 `pprof` 分析后得到 profile 结果
303303

304-
![](https://gitee.com/double12gzh/wiki-pictures/raw/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/%E5%9B%BE1.png)
304+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/2.png)
305305

306-
goroutines运行时不间断,能够完成任务。对于带有`mutex`的程序的配置文件,得到的结果那是完全不同的。
306+
goroutines 运行时不间断,能够完成任务。对于带有 `mutex` 的程序的配置文件,得到的结果那是完全不同的。
307307

308-
![](https://gitee.com/double12gzh/wiki-pictures/raw/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/%E5%9B%BE2.png)
308+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/3.png)
309309

310-
现在运行时间相当零碎,这是由于停放goroutine的mutex造成的。这一点可以从goroutine的概览中得到证实,其中显示了同步时被阻塞的时间(如下图)。
310+
现在运行时间相当零碎,这是由于停放 goroutine 的 mutex 造成的。这一点可以从 goroutine 的概览中得到证实,其中显示了同步时被阻塞的时间(如下图)。
311311

312-
![](https://gitee.com/double12gzh/wiki-pictures/raw/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/%E5%9B%BE3.png)
312+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/4.png)
313313

314-
屏蔽时间大概占到三分之一的时间,这一点可以从下面的block profile的图中详细看到
314+
屏蔽时间大概占到三分之一的时间,这一点可以从下面的 block profile 的图中详细看到
315315

316-
![](https://gitee.com/double12gzh/wiki-pictures/raw/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/%E5%9B%BE4.png)
316+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/Go-How-to-Reduce-Lock-Contention-with-the-Atomic-Package/5.png)
317317

318-
在这种情况下,`atomic`包肯定会带来优势。但是,在某些方面可能会降低性能。例如,如果你要存储一张大地图,每次更新地图时都要复制它,这样效率就很低。
318+
在这种情况下,`atomic` 包肯定会带来优势。但是,在某些方面可能会降低性能。例如,如果你要存储一张大地图,每次更新地图时都要复制它,这样效率就很低。
319319

320-
> 更多关于`mutex`的内容可以参考[Go: Mutex and Starvation](https://medium.com/a-journey-with-go/go-mutex-and-starvation-3f4f4e75ad50)
320+
> 更多关于 `mutex` 的内容可以参考[Go: Mutex and Starvation](https://medium.com/a-journey-with-go/go-mutex-and-starvation-3f4f4e75ad50)
321321
322322
---
323+
323324
via: https://medium.com/a-journey-with-go/go-how-to-reduce-lock-contention-with-the-atomic-package-ba3b2664b549
324325

325326
作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent)
326327
译者:[double12gzh](https://github.com/double12gzh)
327-
校对:[校对者ID](https://github.com/校对者ID)
328+
校对:[lxbwolf](https://github.com/lxbwolf)
328329

329330
本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出

0 commit comments

Comments
 (0)