数据丢失了?我的一百万数据剩下了三十多万,为什么?并发问题
Posted 了 凡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据丢失了?我的一百万数据剩下了三十多万,为什么?并发问题相关的知识,希望对你有一定的参考价值。
博主介绍:
– 我是了凡,喜欢每日在简书上投稿日更的读书感悟笔名:三月_刘超。专注于 Go Web 后端,了解过一些Python、Java、算法、前端等领域。微信公众号【了凡银河系】期待你的关注,企鹅群号(798829931)。未来大家一起加油啊~
背景
都熟知Go语言擅长并发操作,但是对于初步踏入小白的我,今天遇到了一个问题,就是数据丢失了!!
原问题
今日我写了一个小程序进行尝试并发操作,例如:我们使用了10个协程(goroutine),并发的对一个变量进行累加操作,每个goroutine负责执行十万次操作,我们期望最终结果一定是 十个协程 * 十万次累加 = 一百万条数据,下面我们看一下代码描述的是否正确。
程序
package main
import (
"fmt"
"sync"
)
func main() {
var count = 0
// 使用WaitGroup等待10个goroutine完成
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// 对变量count执行10次加1
for j := 0; j < 100000; j++ {
count ++
}
}()
}
// 等待10个goroutine完成
wg.Wait()
fmt.Println(count)
}
结果
我们可以清楚的看到只有 三十多万 的数据没有达到我们期望的数据,为什么呢?
分析问题
首先我们对于下面代码进行分析
go func() {
defer wg.Done()
// 对变量count执行10次加1
for j := 0; j < 100000; j++ {
count ++
}
}()
其他都没有什么问题,我们细看 for 循环内的操作,只有一个 count ++ 操作,如果有一点语言语法基础的同学应该明白 ++ 操作在计算机底层是如何操作的,至少需要三步以上的操作例如:取变量count的当前值,对这个值加1,把结果在保存到count中(详细在下方),这样很显然不是原子操作,既然是原子一定是不可再分割的,所以这样就可能存在并发的问题。
汇编语言里的 count操作
MOVQ "".COUNT(SB), AX
LEAQ 1(AX), CX
MOVQ CX, "".count(SB)
比如,10个goroutine同时读取到 count 的值为9000,接着各自按照自己的逻辑加1,值变成了9001,然后把这个结果再写回到 count 变量。但是,实际上,此时我们增加的总数应该是10才对,这里却只增加了1,好多计数都被“吞”掉了。这是并发访问共享数据的常见错误。
也许这样的问题一些有经验的很容易就能解决,但是如果一些问题没有经验该如何呢?
如何查找类似问题
这个时候介绍 Go 提供了一个检测并发访问共享资源是否有问题的工具:race detector,它可以自动发现程序有没有data race的问题。
使用检测工具:
go run -race main.go
例如:
在编译(compile),测试(test)或者运行(run)Go代码的时候,加上race参数,就有可能发现并发问题。以上结果我们可以清晰的看到具体哪一行有并发问题,就很清晰的可以解决了。当然这个工具开启了race的程序部署在线上,还是比较影响性能的。
解决问题
上面的程序问题就是,共享资源是count变量,临界区是count ++,只要在临界区前面获取锁,在离开临界区的时候释放锁,就能完美地解决data race的问题了。
package main
import (
"fmt"
"sync"
)
func main() {
// 互斥锁保护计算器
var mu sync.Mutex
// 计数器的值
var count = 0
// 使用WaitGroup等待10个goroutine完成
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// 对变量count执行10次加1
for j := 0; j < 100000; j++ {
mu.Lock()
count ++
mu.Unlock()
}
}()
}
// 等待10个goroutine完成
wg.Wait()
fmt.Println(count)
}
结果
可以发现正常执行,加上data race也没有警告问题,完美解决。
总结
- 并发访问共享数据的常见错误的解决方案
- 使用 race detector(
go run -race main.go
)可以检查并发问题
这次就先讲到这里,如果想要了解更多的golang语言内容一键三连后序每周持续更新!微信公众号【了凡银河系】期待你的关注。
以上是关于数据丢失了?我的一百万数据剩下了三十多万,为什么?并发问题的主要内容,如果未能解决你的问题,请参考以下文章
历时三个月,少说有三十多万字的《从零开始学习Java设计模式》小白零基础设计模式入门导读(强烈建议收藏)
历时三个月,少说有三十多万字的《从零开始学习Java设计模式》小白零基础设计模式入门导读(强烈建议收藏)
sqlserver数据库登录日志LOG目录下的SQLDump10000.txt文件有三十多G,我想问一下这个文件能删除吗?
#yyds干货盘点# 前端歌谣的刷题之路-第一百三十题-absolute