This article mainly introduces to you the relevant information about for-loop and goroutine issues in Golang. The article introduces it in detail through sample code. It has certain reference learning value for everyone to learn or use golang. Friends who need it Let’s learn with the editor below.
Background
#Recently, while studying MIT’s distributed course 6.824, I encountered some problems when using Go to implement the Raft protocol. question. I share it for everyone’s reference and study. I won’t say much below, let’s take a look at the detailed introduction.
See the following code:
for i := 0; i < len(rf.peers); i++ { DPrintf("i = %d", i) if i == rf.me { DPrintf("skipping myself #%d", rf.me) continue } go func() { DPrintf("len of rf.peers = %d", len(rf.peers)) DPrintf("server #%d sending request vote to server %d", rf.me, i) reply := &RequestVoteReply{} ok := rf.sendRequestVote(i, args, reply) if ok && reply.VoteGranted && reply.Term == rf.currentTerm { rf.voteCount++ if rf.voteCount > len(rf.peers)/2 { rf.winElectionCh <- true } } }() }
Among them, the length of the peers slice is 3, so the highest subscript is 2. The for-loop in the code in parallel programming should be very intuitive, and I didn't realize there was any problem at the time. However, during the debugging process, index out of bounds errors kept being reported. The debugging information showed that the value of i was 3. At that time, I still couldn’t figure out how the loop condition could become 3 when it was clearly i < 2.
Analysis
Although I don’t understand what happened, I know that it should be caused by the goroutine introduced in the loop. After Google, I found that there is a page in Go's wiki called Common Mistake - Using goroutines on loop iterator variables that specifically mentions this problem. It seems to be really common. I laugh and cry~
Beginners often use it Use the following code to process data in parallel:
for val := range values { go val.MyMethod() }
Or use closure:
for val := range values { go func() { fmt.Println(val) }() }
The problem here The reason is that val is actually a single variable that iterates through all the data in the slice. Since the closure is only bound to this val variable, it is very likely that the result of the above code is that all goroutines output the last element of the slice. This is because it is very likely that the goroutine will not start executing until after the for-loop is executed, and at this time the value of val points to the last element in the slice.
The val variable in the above loops is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.
Solution
The correct way to write the above code is:
for val := range values { go func(val interface{}) { fmt.Println(val) }(val) }
Here, val is passed into the goroutine as a parameter. Each val will be independently calculated and saved to the goroutine's stack, thereby obtaining the expected results.
Another method is to define new variables within the loop. Since the variables defined within the loop are not shared during the loop traversal, the same effect can be achieved:
for i := range valslice { val := valslice[i] go func() { fmt.Println(val) }() }
For the problem mentioned at the beginning of the article, the simplest solution is to add a temporary variable in the loop and replace all i in the subsequent goroutine with this temporary variable:
server := i
Summary
The above is the detailed content of Detailed examples of for-loop and goroutine in Golang. For more information, please follow other related articles on the PHP Chinese website!