Blogger introduction:
- I'm Li Fan. I like to contribute to the simple book every day. The pseudonym of reading comprehension: March_ Liu Chao . Focus on the Go Web backend, and have learned about Python, Java, algorithms, front-end and other fields. The Milky way WeChat official account is waiting for your attention, Penguin Group Number (798829931). Come on in the future~
background
Everyone is familiar with Go language and is good at concurrent operation, but for me who initially stepped into Xiaobai, I encountered a problem today, that is, data loss!!
Original problem
Today, I wrote a small program to try concurrent operations. For example, we use 10 goroutines to accumulate a variable concurrently. Each goroutine is responsible for 100000 operations. We expect that the final result must be 10 coroutines * 100000 accumulations = 1 million data. Let's see whether the code description is correct.
program
package main import ( "fmt" "sync" ) func main() { var count = 0 // Use WaitGroup to wait for 10 goroutine s to complete var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() // Execute the variable count 10 times plus 1 for j := 0; j < 100000; j++ { count ++ } }() } // Wait for 10 goroutine s to complete wg.Wait() fmt.Println(count) }
result
We can clearly see that only more than 300000 data do not meet our expectations. Why?
Analyze problems
First, we analyze the following code
go func() { defer wg.Done() // Execute the variable count 10 times plus 1 for j := 0; j < 100000; j++ { count ++ } }()
There are no other problems. Let's take a closer look at the operation in the for loop. There is only one count + + operation. If you have a little basic language syntax, you should understand how the + + operation operates at the bottom of the computer. At least three steps are required. For example, take the current value of the variable count, add 1 to this value, and save the result in count (see the details below), This is obviously not an atomic operation. Since atoms must be indivisible, there may be a problem of concurrency.
count operation in assembly language
MOVQ "".COUNT(SB), AX LEAQ 1(AX), CX MOVQ CX, "".count(SB)
For example, 10 goroutine s read the value of count as 9000 at the same time, then add 1 according to their own logic, and the value becomes 9001, and then write the result back to the count variable. However, in fact, the total number we should increase at this time should be 10, but only 1 is increased here, and many counts are "swallowed". This is a common error in accessing shared data concurrently.
Maybe some experienced problems can be solved easily, but what if some problems have no experience?
How to find similar problems
At this time, Go provides a tool to detect whether there is a problem with concurrent access to shared resources: race detector, which can automatically find whether there is a data race problem in the program.
Use test tools:
go run -race main.go
For example:
When compiling, test ing or run ning Go code, adding the race parameter may find concurrency problems. From the above results, we can clearly see which line has concurrency problems, and we can clearly solve them. Of course, this tool enables the application of race to be deployed online, which still affects the performance.
solve the problem
The above program problem is that the shared resources are count variables and the critical area is count + +. As long as the lock is obtained in front of the critical area and released when leaving the critical area, the problem of data race can be solved perfectly.
package main import ( "fmt" "sync" ) func main() { // Mutex protection calculator var mu sync.Mutex // Counter value var count = 0 // Use WaitGroup to wait for 10 goroutine s to complete var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() // Execute the variable count 10 times plus 1 for j := 0; j < 100000; j++ { mu.Lock() count ++ mu.Unlock() } }() } // Wait for 10 goroutine s to complete wg.Wait() fmt.Println(count) }
result
It can be found that the data race runs normally, and there are no warnings. The problem is solved perfectly.
summary
- Solutions to common errors in concurrent access to shared data
- Use race detector (go run -race main.go) to check for concurrency problems
Let's stop here this time. If you want to know more about the content of golang language, the sequence will be updated continuously every week after one click and three connections! The Milky way WeChat official account is waiting for your attention.