当前位置:网站首页>n++也不靠谱
n++也不靠谱
2022-07-04 12:34:00 【小锟哥哥】

今天小明又去面试了,又被问了一个奇怪的面试题:
n := 0
for i := 0; i < 1000000; i++ {
go func() {
n++
}()
}
fmt.Println(n)
到你思考的时间了,输出啥结果呢?
小明思考了许久,给出了他的回答:不知道,然后面试官就告诉他:你通过了。
是不是有点离谱,没错,这个代码的结果就是不知道,每次执行的结果都不一样,全看 cpu 咋调度。
且听我来给客官慢慢道来。
一、最开始的原型
我们根据面试代码,往回滚一点,看下这样的代码:
n := 0
for i := 0; i < 1000000; i++ {
func() {
n++
}()
}
fmt.Println(n)
我们把协程拿掉,现在的结果是不是就很好知道了,没错就是循环的次数 1000000。
二、里面的坑
我们再回到面试的代码,这里面其实有两个坑:
第一个坑:他没加协程等待,所以很可能一扫而过,还没循环几次主程序就结束了,甚至是一次循环都没做就退出了。
但是在面试中,一般不提这个坑,这不是面试的重点,当然你也可以提一下。
第二个坑就是面试的重点了:
在不考虑主线程提前退出的问题,就是加入协程后,n++ 的结果不准确了。
为什么呢?
因为 n++ 并不是原子的,他要完成 n++ 的操作他需要做三步:
从内存里面取出值 执行 +1 操作 赋值回去
因为他不是原子的,所以很可能在你取值的时候别的线层也在取值,也在进行计算,最后赋值时就会被覆盖,从而出现随机不可预算的结果。
三、该怎么保证结果呢?
因为 n++ 不是原子的,如果我们要让他变原子,常见的操作有两种:
1、加锁
首先我们为了保证他能把循环执行完毕,需要加个 wait:
wg := sync.WaitGroup{}
n := 0
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
n++ //不是原子的 1、从内存读出 2、n++ 3、赋值
}()
}
wg.Wait()
fmt.Println(n)
这样就能让他执行完毕了,再加入我们的线层锁:
wg := sync.WaitGroup{}
locker := sync.Mutex{}
n := 0
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// 锁
defer locker.Unlock()
locker.Lock()
n++ //不是原子的 1、从内存读出 2、n++ 3、赋值
}()
}
wg.Wait()
fmt.Println(n)
这样执行的结果,每次都是执行的次数了。
2、使用 atomic
我们偶尔还会使用 atomic 包来处理这类操作,但是也有一定局限,他支持的数据类型有限。
直接上代码:
var n int32 = 0
for i := 0; i < 1000000; i++ {
func() {
atomic.AddInt32(&n, 1) //原子操作
}()
}
fmt.Println(n)
这里我们把 n 变成了 int32 类型,这样的运行结果也能保证是循环的次数。
边栏推荐
- MySQL performance optimization index
- 从0到1建设智能灰度数据体系:以vivo游戏中心为例
- Fastlane 一键打包/发布APP - 使用记录及踩坑
- ISO 27001 Information Security Management System Certification
- C language function
- 17. Memory partition and paging
- Article download address
- PKCs 5: password based cryptography specification version 2.1 Chinese Translation
- 众昂矿业:为保障萤石足量供应,开源节流势在必行
- Can Console. Clear be used to only clear a line instead of whole console?
猜你喜欢
Servlet learning notes
01. Basics - MySQL overview
CANN算子:利用迭代器高效实现Tensor数据切割分块处理
Jetson TX2 configures common libraries such as tensorflow and pytoch
ArcGIS uses grid processing tools for image clipping
A treasure open source software, cross platform terminal artifact tabby
Tableau makes data summary after linking the database, and summary exceptions occasionally occur.
How to realize the function of Sub Ledger of applet?
强化学习-学习笔记1 | 基础概念
When synchronized encounters this thing, there is a big hole, pay attention!
随机推荐
Global and Chinese markets for soluble suture 2022-2028: Research Report on technology, participants, trends, market size and share
17. Memory partition and paging
C#/VB.NET 给PDF文档添加文本/图像水印
16.内存使用与分段
[notes] in depth explanation of assets, resources and assetbundles
AI 绘画极简教程
ArgMiner:一个用于对论点挖掘数据集进行处理、增强、训练和推理的 PyTorch 的包
轻松玩转三子棋
Entity framework calls Max on null on records - Entity Framework calling Max on null on records
C语言数组
MYCAT middleware installation and use
二分查找的简单理解
Fly tutorial 02 advanced functions of elevatedbutton (tutorial includes source code) (tutorial includes source code)
Talk about "in C language"
Article download address
C language: find the length of string
七、软件包管理
How to realize the function of Sub Ledger of applet?
runc hang 导致 Kubernetes 节点 NotReady
6 分钟看完 BGP 协议。