当前位置:网站首页>N++ is not reliable

N++ is not reliable

2022-07-04 13:09:00 Brother Xiaokun

Xiao Ming went to the interview again today , I was asked another strange interview question :

n := 0
for i := 0; i < 1000000; i++ {
 go func() {
  n++
 }()
}
fmt.Println(n)

It's time for you to think , What results are output ?

Xiao Ming thought for a long time , Gave his answer : I do not know! , Then the interviewer told him : You passed .

Isn't it a little crazy , you 're right , The result of this code is that I don't know , The result of each execution is different , Look at it all cpu How to dispatch .

Let me tell you something slowly .

One 、 The original prototype

According to the interview code , Roll back a little , Look at this code :

n := 0
for i := 0; i < 1000000; i++ {
 func() {
  n++
 }()
}
fmt.Println(n)

Let's get rid of Xie Cheng , Now the result is not very good to know , Yes, it's the number of cycles 1000000.

Two 、 The pit inside

Let's go back to the interview code , There are actually two pits :

The first pit : He didn't wait for it , So it is likely to sweep away , The main program is over before it has been cycled several times , Even quit without doing a cycle .

But in the interview , Generally, this pit is not mentioned , This is not the point of the interview , Of course, you can also mention .

The second pit is the focus of the interview :

Without considering the early exit of the main thread , After joining the process ,n++ The result of is not accurate .

Why? ?

because n++ Not atomic , He wants to finish n++ He needs to do three steps :

  • Get the value from memory
  • perform +1 operation
  • Assign back

Because it's not atomic , So it is likely that other line layers are also taking values when you take values , Also calculating , The last assignment will be overwritten , Thus, random and unpredictable results appear .

3、 ... and 、 How to guarantee the result ?

because n++ It's not atomic , If we want him to become an atom , There are two common operations :

1、 Lock

First of all, we want to ensure that he can finish the cycle , I need to add one wait:

wg := sync.WaitGroup{}
n := 0
for i := 0; i < 1000000; i++ {
 wg.Add(1)
 go func() {
  defer wg.Done()
  n++ // It's not atomic  1、 Read from memory  2、n++ 3、 assignment
 }()
}
wg.Wait()
fmt.Println(n)

In this way, he can finish the execution , Then add our thread layer lock :

wg := sync.WaitGroup{}
locker := sync.Mutex{}
n := 0
for i := 0; i < 1000000; i++ {
 wg.Add(1)
 go func() {
  defer wg.Done()
  //  lock
  defer locker.Unlock()
  locker.Lock()
  n++ // It's not atomic  1、 Read from memory  2、n++ 3、 assignment
 }()
}
wg.Wait()
fmt.Println(n)

The result of this execution , Each time is the number of executions .

2、 Use atomic

We occasionally use atomic Package to handle such operations , But there are certain limitations , The data types he supports are limited .

Go straight to the code :

var n int32 = 0
for i := 0; i < 1000000; i++ {
 func() {
  atomic.AddInt32(&n, 1// Atomic manipulation
 }()
}
fmt.Println(n)

Here we put n Turned into int32 type , Such operation result can also guarantee the number of cycles .

原网站

版权声明
本文为[Brother Xiaokun]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207041131476815.html