当前位置:网站首页>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 类型,这样的运行结果也能保证是循环的次数。
边栏推荐
- AbstractDispatcherServletInitializer 的实现类为什么可以在初始化Web容器的时候被调用
- Concepts and theories related to distributed transactions
- Interview question MySQL transaction (TCL) isolation (four characteristics)
- MYCAT middleware installation and use
- Is the main thread the same as the UI thread- Is main thread the same as UI thread?
- Langue C: trouver le nombre de palindromes dont 100 - 999 est un multiple de 7
- 从0到1建设智能灰度数据体系:以vivo游戏中心为例
- 6 分钟看完 BGP 协议。
- 面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
- ASP. Net razor – introduction to VB loops and arrays
猜你喜欢
随机推荐
Error: Failed to download metadata for repo ‘AppStream‘: Cannot download repomd. XML solution
Fundamentals of container technology
BackgroundWorker用法示例
01. Basics - MySQL overview
Global and Chinese market of cardiac monitoring 2022-2028: Research Report on technology, participants, trends, market size and share
CANN算子:利用迭代器高效实现Tensor数据切割分块处理
The most robust financial products in 2022
MDK在头文件中使用预编译器时,#ifdef 无效的问题
[Yu Yue education] 233 pre school children's language education reference questions in the spring of 2019 of the National Open University
Langue C: trouver le nombre de palindromes dont 100 - 999 est un multiple de 7
golang 设置goproxy代理的小细节,适用于go module下载超时,阿里云镜像go module下载超时
PostgreSQL 9.1 飞升之路
R语言--readr包读写数据
jsonp
asp. Core is compatible with both JWT authentication and cookies authentication
Runc hang causes the kubernetes node notready
MySQL performance optimization index
Pat 1059 prime factors (25 points) prime table
mm_ Cognition of struct structure
Fastlane one click package / release app - usage record and stepping on pit